Skip to content

Commit 7e24c14

Browse files
authored
Merge pull request #6132 from dotty-staging/change-overload-lambda
Avoid forcing lambda parameter types in overloading resolution
2 parents a9ef81f + cd9e7e1 commit 7e24c14

File tree

15 files changed

+160
-122
lines changed

15 files changed

+160
-122
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -195,21 +195,16 @@ object messages {
195195
else
196196
""
197197

198+
val inferred =
199+
if (pt == WildcardType) ""
200+
else i"\nWhat I could infer was: $pt"
201+
198202
i"""Missing parameter type
199203
|
200-
|The argument types of an anonymous function must be fully known. (SLS 8.5)
201-
|Expected type: $pt
202-
|Missing type for parameter ${param.name}$ofFun"""
204+
|I could not infer the type of the parameter ${param.name}$ofFun.$inferred"""
203205
}
204206

205-
val explanation: String =
206-
hl"""|Anonymous functions must define a type. For example, if you define a function like this one:
207-
|
208-
|${"val f = { case x: Int => x + 1 }"}
209-
|
210-
|Make sure you give it a type of what you expect to match and help the type inference system:
211-
|
212-
|${"val f: Any => Int = { case x: Int => x + 1 }"} """
207+
val explanation: String = ""
213208
}
214209

215210
case class WildcardOnTypeArgumentNotAllowedOnNew()(implicit ctx: Context)

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

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
832832
case funRef: TermRef =>
833833
val app =
834834
if (proto.allArgTypesAreCurrent())
835-
new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
835+
new ApplyToTyped(tree, fun1, funRef, proto.unforcedTypedArgs, pt)
836836
else
837837
new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree))
838838
convertNewGenericArray(app.result)
@@ -857,7 +857,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
857857
}
858858

859859
fun1.tpe match {
860-
case err: ErrorType => cpy.Apply(tree)(fun1, proto.typedArgs).withType(err)
860+
case err: ErrorType => cpy.Apply(tree)(fun1, proto.unforcedTypedArgs).withType(err)
861861
case TryDynamicCallType => typedDynamicApply(tree, pt)
862862
case _ =>
863863
if (originalProto.isDropped) fun1
@@ -1604,7 +1604,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
16041604
if (isDetermined(alts2)) alts2
16051605
else {
16061606
pretypeArgs(alts2, pt)
1607-
narrowByTrees(alts2, pt.typedArgs, resultType)
1607+
narrowByTrees(alts2, pt.unforcedTypedArgs, resultType)
16081608
}
16091609
}
16101610

@@ -1665,7 +1665,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
16651665
else pt match {
16661666
case pt @ FunProto(_, resType: FunProto) =>
16671667
// try to narrow further with snd argument list
1668-
val advanced = advanceCandidates(pt.typedArgs.tpes)
1668+
val advanced = advanceCandidates(pt.unforcedTypedArgs.tpes)
16691669
resolveOverloaded(advanced.map(_._1), resType, Nil) // resolve with candidates where first params are stripped
16701670
.map(advanced.toMap) // map surviving result(s) back to original candidates
16711671
case _ =>
@@ -1697,40 +1697,38 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
16971697
private def pretypeArgs(alts: List[TermRef], pt: FunProto)(implicit ctx: Context): Unit = {
16981698
def recur(altFormals: List[List[Type]], args: List[untpd.Tree]): Unit = args match {
16991699
case arg :: args1 if !altFormals.exists(_.isEmpty) =>
1700-
def isUnknownParamType(t: untpd.Tree) = t match {
1701-
case ValDef(_, tpt, _) => tpt.isEmpty
1702-
case _ => false
1703-
}
1704-
val fn = untpd.functionWithUnknownParamType(arg)
1705-
if (fn.isDefined) {
1706-
def isUniform[T](xs: List[T])(p: (T, T) => Boolean) = xs.forall(p(_, xs.head))
1707-
val formalsForArg: List[Type] = altFormals.map(_.head)
1708-
def argTypesOfFormal(formal: Type): List[Type] =
1709-
formal match {
1710-
case defn.FunctionOf(args, result, isImplicit, isErased) => args
1711-
case defn.PartialFunctionOf(arg, result) => arg :: Nil
1712-
case _ => Nil
1700+
untpd.functionWithUnknownParamType(arg) match {
1701+
case Some(fn) =>
1702+
def isUniform[T](xs: List[T])(p: (T, T) => Boolean) = xs.forall(p(_, xs.head))
1703+
val formalsForArg: List[Type] = altFormals.map(_.head)
1704+
def argTypesOfFormal(formal: Type): List[Type] =
1705+
formal match {
1706+
case defn.FunctionOf(args, result, isImplicit, isErased) => args
1707+
case defn.PartialFunctionOf(arg, result) => arg :: Nil
1708+
case _ => Nil
1709+
}
1710+
val formalParamTypessForArg: List[List[Type]] =
1711+
formalsForArg.map(argTypesOfFormal)
1712+
if (formalParamTypessForArg.forall(_.nonEmpty) &&
1713+
isUniform(formalParamTypessForArg)((x, y) => x.length == y.length)) {
1714+
val commonParamTypes = formalParamTypessForArg.transpose.map(ps =>
1715+
// Given definitions above, for i = 1,...,m,
1716+
// ps(i) = List(p_i_1, ..., p_i_n) -- i.e. a column
1717+
// If all p_i_k's are the same, assume the type as formal parameter
1718+
// type of the i'th parameter of the closure.
1719+
if (isUniform(ps)(_ frozen_=:= _)) ps.head
1720+
else WildcardType)
1721+
def isPartial = // we should generate a partial function for the arg
1722+
fn.isInstanceOf[untpd.Match] &&
1723+
formalsForArg.exists(_.isRef(defn.PartialFunctionClass))
1724+
val commonFormal =
1725+
if (isPartial) defn.PartialFunctionOf(commonParamTypes.head, WildcardType)
1726+
else defn.FunctionOf(commonParamTypes, WildcardType)
1727+
overload.println(i"pretype arg $arg with expected type $commonFormal")
1728+
if (commonParamTypes.forall(isFullyDefined(_, ForceDegree.noBottom)))
1729+
pt.typedArg(arg, commonFormal)(ctx.addMode(Mode.ImplicitsEnabled))
17131730
}
1714-
val formalParamTypessForArg: List[List[Type]] =
1715-
formalsForArg.map(argTypesOfFormal)
1716-
if (formalParamTypessForArg.forall(_.nonEmpty) &&
1717-
isUniform(formalParamTypessForArg)((x, y) => x.length == y.length)) {
1718-
val commonParamTypes = formalParamTypessForArg.transpose.map(ps =>
1719-
// Given definitions above, for i = 1,...,m,
1720-
// ps(i) = List(p_i_1, ..., p_i_n) -- i.e. a column
1721-
// If all p_i_k's are the same, assume the type as formal parameter
1722-
// type of the i'th parameter of the closure.
1723-
if (isUniform(ps)(_ frozen_=:= _)) ps.head
1724-
else WildcardType)
1725-
def isPartial = // we should generate a partial function for the arg
1726-
fn.get.isInstanceOf[untpd.Match] &&
1727-
formalsForArg.exists(_.isRef(defn.PartialFunctionClass))
1728-
val commonFormal =
1729-
if (isPartial) defn.PartialFunctionOf(commonParamTypes.head, WildcardType)
1730-
else defn.FunctionOf(commonParamTypes, WildcardType)
1731-
overload.println(i"pretype arg $arg with expected type $commonFormal")
1732-
pt.typedArg(arg, commonFormal)(ctx.addMode(Mode.ImplicitsEnabled))
1733-
}
1731+
case None =>
17341732
}
17351733
recur(altFormals.map(_.tail), args1)
17361734
case _ =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ object ErrorReporting {
4747
case _: WildcardType | _: IgnoredProto => ""
4848
case tp => em" and expected result type $tp"
4949
}
50-
em"arguments (${tp.typedArgs.tpes}%, %)$result"
50+
em"arguments (${tp.unforcedTypedArgs.tpes}%, %)$result"
5151
case _ =>
5252
em"expected type $tp"
5353
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,7 +1078,7 @@ class Namer { typer: Typer =>
10781078
}
10791079

10801080
/** Typecheck `tree` during completion using `typed`, and remember result in TypedAhead map */
1081-
def typedAheadImpl(tree: Tree, typed: untpd.Tree => tpd.Tree)(implicit ctx: Context): tpd.Tree = {
1081+
def typedAhead(tree: Tree, typed: untpd.Tree => tpd.Tree)(implicit ctx: Context): tpd.Tree = {
10821082
val xtree = expanded(tree)
10831083
xtree.getAttachment(TypedAhead) match {
10841084
case Some(ttree) => ttree
@@ -1090,10 +1090,10 @@ class Namer { typer: Typer =>
10901090
}
10911091

10921092
def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
1093-
typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrTypeBits addMode Mode.Type))
1093+
typedAhead(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrTypeBits addMode Mode.Type))
10941094

10951095
def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
1096-
typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrTypeBits))
1096+
typedAhead(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrTypeBits))
10971097

10981098
def typedAheadAnnotation(tree: Tree)(implicit ctx: Context): tpd.Tree =
10991099
typedAheadExpr(tree, defn.AnnotationType)

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

Lines changed: 27 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,6 @@ object ProtoTypes {
232232
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
233233
var typedArg: SimpleIdentityMap[untpd.Tree, Tree] = SimpleIdentityMap.Empty
234234

235-
/** A map recording the typer states and constraints in which arguments stored in myTypedArg were typed */
236-
var evalState: SimpleIdentityMap[untpd.Tree, (TyperState, Constraint)] = SimpleIdentityMap.Empty
237-
238235
/** The tupled version of this prototype, if it has been computed */
239236
var tupled: Type = NoType
240237

@@ -265,66 +262,51 @@ object ProtoTypes {
265262

266263
override def notApplied: Type = WildcardType
267264

268-
/** Forget the types of any arguments that have been typed producing a constraint
269-
* - that is in a typer state that is not yet committed into the one of the current context `ctx`,
270-
* - or that has been retracted from its typestate because oif a failed operation.
271-
* This is necessary to avoid "orphan" TypeParamRefs that are referred to from
272-
* type variables in the typed arguments, but that are not registered in the
273-
* current constraint. Test cases are pos/t1756.scala and pos/i3538.scala.
274-
* @return True if all arguments have types (in particular, no types were forgotten).
265+
/** @return True if all arguments have types.
275266
*/
276-
def allArgTypesAreCurrent()(implicit ctx: Context): Boolean = {
277-
state.evalState foreachBinding { (arg, tstateConstr) =>
278-
if ((tstateConstr._1.uncommittedAncestor.constraint `ne` ctx.typerState.constraint) ||
279-
tstateConstr._2.isRetracted) {
280-
typr.println(i"need to invalidate $arg / ${state.typedArg(arg)}, ${tstateConstr._2}, current = ${ctx.typerState.constraint}")
281-
state.typedArg = state.typedArg.remove(arg)
282-
state.evalState = state.evalState.remove(arg)
283-
}
284-
}
267+
def allArgTypesAreCurrent()(implicit ctx: Context): Boolean =
285268
state.typedArg.size == args.length
269+
270+
private def isUndefined(tp: Type): Boolean = tp match {
271+
case _: WildcardType => true
272+
case defn.FunctionOf(args, result, _, _) => args.exists(isUndefined) || isUndefined(result)
273+
case _ => false
286274
}
287275

288276
private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree, force: Boolean)(implicit ctx: Context): Tree = {
289277
var targ = state.typedArg(arg)
290278
if (targ == null) {
291-
if (!force && untpd.functionWithUnknownParamType(arg).isDefined)
292-
// If force = false, assume ? rather than reporting an error.
293-
// That way we don't cause a "missing parameter" error in `typerFn(arg)`
294-
targ = arg.withType(WildcardType)
295-
else {
296-
targ = typerFn(arg)
297-
if (!ctx.reporter.hasUnreportedErrors) {
298-
// FIXME: This can swallow warnings by updating the typerstate from a nested
299-
// context that gets discarded later. But we do have to update the
300-
// typerstate if there are no errors. If we also omitted the next two lines
301-
// when warning were emitted, `pos/t1756.scala` would fail when run with -feature.
302-
// It would produce an orphan type parameter for CI when pickling.
303-
state.typedArg = state.typedArg.updated(arg, targ)
304-
state.evalState = state.evalState.updated(arg, (ctx.typerState, ctx.typerState.constraint))
305-
}
279+
untpd.functionWithUnknownParamType(arg) match {
280+
case Some(untpd.Function(args, _)) if !force =>
281+
// If force = false, assume what we know about the parameter types rather than reporting an error.
282+
// That way we don't cause a "missing parameter" error in `typerFn(arg)`
283+
val paramTypes = args map {
284+
case ValDef(_, tpt, _) if !tpt.isEmpty => typer.typedType(tpt).typeOpt
285+
case _ => WildcardType
286+
}
287+
targ = arg.withType(defn.FunctionOf(paramTypes, WildcardType))
288+
case Some(_) if !force =>
289+
targ = arg.withType(WildcardType)
290+
case _ =>
291+
targ = typerFn(arg)
292+
if (!ctx.reporter.hasUnreportedErrors)
293+
state.typedArg = state.typedArg.updated(arg, targ)
306294
}
307295
}
308296
targ
309297
}
310298

311299
/** The typed arguments. This takes any arguments already typed using
312300
* `typedArg` into account.
313-
* @param force if true try to typecheck arguments even if they are functions
314-
* with unknown parameter types - this will then cause a
315-
* "missing parameter type" error
316301
*/
317-
private def typedArgs(force: Boolean): List[Tree] =
302+
def unforcedTypedArgs: List[Tree] =
318303
if (state.typedArgs.size == args.length) state.typedArgs
319304
else {
320-
val args1 = args.mapconserve(cacheTypedArg(_, typer.typed(_), force))
321-
if (force || !args1.contains(WildcardType)) state.typedArgs = args1
305+
val args1 = args.mapconserve(cacheTypedArg(_, typer.typed(_), force = false))
306+
if (!args1.exists(arg => isUndefined(arg.tpe))) state.typedArgs = args1
322307
args1
323308
}
324309

325-
def typedArgs: List[Tree] = typedArgs(force = true)
326-
def unforcedTypedArgs: List[Tree] = typedArgs(force = false)
327-
328310
/** Type single argument and remember the unadapted result in `myTypedArg`.
329311
* used to avoid repeated typings of trees when backtracking.
330312
*/
@@ -376,7 +358,7 @@ object ProtoTypes {
376358
derivedFunProto(args, tm(resultType), typer)
377359

378360
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T =
379-
ta(ta.foldOver(x, typedArgs.tpes), resultType)
361+
ta(ta.foldOver(x, unforcedTypedArgs.tpes), resultType)
380362

381363
override def deepenProto(implicit ctx: Context): FunProto = derivedFunProto(args, resultType.deepenProto, typer)
382364

@@ -390,7 +372,7 @@ object ProtoTypes {
390372
* [](args): resultType, where args are known to be typed
391373
*/
392374
class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer, isContextual: Boolean)(implicit ctx: Context) extends FunProto(args, resultType)(typer, isContextual)(ctx) {
393-
override def typedArgs: List[tpd.Tree] = args
375+
override def unforcedTypedArgs: List[tpd.Tree] = args
394376
override def withContext(ctx: Context): FunProtoTyped = this
395377
}
396378

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ class Typer extends Namer
525525
templ1 = cpy.Template(templ)(parents = untpd.TypeTree(pt) :: Nil)
526526
templ1.parents foreach {
527527
case parent: RefTree =>
528-
typedAheadImpl(parent, tree => inferTypeParams(typedType(tree), pt))
528+
typedAhead(parent, tree => inferTypeParams(typedType(tree), pt))
529529
case _ =>
530530
}
531531
val x = tpnme.ANON_CLASS
@@ -940,7 +940,7 @@ class Typer extends Namer
940940
}
941941
case _ =>
942942
}
943-
errorType(AnonymousFunctionMissingParamType(param, params, tree, pt), param.sourcePos)
943+
errorType(AnonymousFunctionMissingParamType(param, params, tree, formal), param.sourcePos)
944944
}
945945

946946
def protoFormal(i: Int): Type =
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
scala> val xs = scala.collection.mutable.ListBuffer[Int]
22
1 | val xs = scala.collection.mutable.ListBuffer[Int]
33
| ^
4-
|Missing parameter type
4+
| Missing parameter type
55
|
6-
|The argument types of an anonymous function must be fully known. (SLS 8.5)
7-
|Expected type: ?
8-
|Missing type for parameter elems
6+
| I could not infer the type of the parameter elems.
97
scala> val xs = scala.collection.mutable.ListBuffer[Int]()
108
val xs: scala.collection.mutable.ListBuffer[Int] = ListBuffer()

0 commit comments

Comments
 (0)