Skip to content

Commit 914787c

Browse files
committed
Fix #8111: Improve inferredParam
An inferred parameter type I has two passible sources: - the type S known from the context - the type T known from the callee `f` if the lambda is of a form like `x => f(x)` If `T` exists, we know that `S <: I <: T`. In this commit we make use of this information to be more intelligent how the type I is inferred.
1 parent 4c84ee8 commit 914787c

File tree

2 files changed

+40
-21
lines changed

2 files changed

+40
-21
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,7 @@ class Typer extends Namer
10091009
yield param.name -> idx
10101010
}.toMap
10111011
if (paramIndex.size == params.length)
1012-
expr match {
1012+
expr match
10131013
case untpd.TypedSplice(expr1) =>
10141014
expr1.tpe
10151015
case _ =>
@@ -1023,7 +1023,6 @@ class Typer extends Namer
10231023
nestedCtx.typerState.commit()
10241024
fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args)
10251025
expr1.tpe
1026-
}
10271026
else NoType
10281027
case _ =>
10291028
NoType
@@ -1041,28 +1040,38 @@ class Typer extends Namer
10411040

10421041
val (protoFormals, resultTpt) = decomposeProtoFunction(pt, params.length)
10431042

1044-
/** Two attempts: First, if expected type is fully defined pick this one.
1045-
* Second, if function is of the form
1046-
* (x1, ..., xN) => f(... x1, ..., XN, ...)
1047-
* where each `xi` occurs exactly once in the argument list of `f` (in
1048-
* any order), and f has a method type MT, pick the corresponding parameter
1049-
* type in MT, if this one is fully defined.
1050-
* If both attempts fail, issue a "missing parameter type" error.
1051-
*/
1052-
def inferredParamType(param: untpd.ValDef, formal: Type): Type = {
1053-
if (isFullyDefined(formal, ForceDegree.noBottom)) return formal
1054-
calleeType.widen match {
1043+
/** The inferred parameter type for a parameter in a lambda that does
1044+
* not have an explicit type given.
1045+
* An inferred parameter type I has two possible sources:
1046+
* - the type S known from the context
1047+
* - the "target type" T known from the callee `f` if the lambda is of a form like `x => f(x)`
1048+
* If `T` exists, we know that `S <: I <: T`.
1049+
*
1050+
* The inference makes three attempts:
1051+
*
1052+
* 1. If the expected type `S` is already fully defined pick this one.
1053+
* 2. Compute the target type `T` and make it known that `S <: T`.
1054+
* If the expected type `S` can be fully defined under ForceDegree.noBottom,
1055+
* pick this one (this might use the fact that S <: T for an upper approximation).
1056+
* 3. Otherwise, if the target type `T` can be fully defined under ForceDegree.noBottom,
1057+
* pick this one.
1058+
*
1059+
* If all attempts fail, issue a "missing parameter type" error.
1060+
*/
1061+
def inferredParamType(param: untpd.ValDef, formal: Type): Type =
1062+
if isFullyDefined(formal, ForceDegree.none) then return formal
1063+
val target = calleeType.widen match
10551064
case mtpe: MethodType =>
10561065
val pos = paramIndex(param.name)
1057-
if (pos < mtpe.paramInfos.length) {
1066+
if pos < mtpe.paramInfos.length then
10581067
val ptype = mtpe.paramInfos(pos)
1059-
if (isFullyDefined(ptype, ForceDegree.noBottom) && !ptype.isRepeatedParam)
1060-
return ptype
1061-
}
1062-
case _ =>
1063-
}
1064-
errorType(AnonymousFunctionMissingParamType(param, params, tree, formal), param.sourcePos)
1065-
}
1068+
if ptype.isRepeatedParam then NoType else ptype
1069+
else NoType
1070+
case _ => NoType
1071+
if target.exists then formal <:< target
1072+
if isFullyDefined(formal, ForceDegree.noBottom) then formal
1073+
else if target.exists && isFullyDefined(target, ForceDegree.noBottom) then target
1074+
else errorType(AnonymousFunctionMissingParamType(param, params, tree, formal), param.sourcePos)
10661075

10671076
def protoFormal(i: Int): Type =
10681077
if (protoFormals.length == params.length) protoFormals(i)

tests/pos/i8111.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object Example extends App {
2+
3+
def assertLazy[A, B](f: (A) => B): Boolean = ???
4+
5+
def fromEither[E, F](eea: Either[E, F]): Unit = ???
6+
7+
lazy val result = assertLazy(fromEither)
8+
9+
println("It compiles!")
10+
}

0 commit comments

Comments
 (0)