Skip to content

Commit aef3d57

Browse files
committed
Always take results into account for resolveOverloading
Previously, this was not done when resolving through tpd. Also, improve comments to explain why we picked the "check afterwards" strategy. Finally, refactor so that the new logic is all inside resolveOverloaded.
1 parent 54835b6 commit aef3d57

File tree

2 files changed

+54
-47
lines changed

2 files changed

+54
-47
lines changed

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

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,53 @@ trait Applications extends Compatibility { self: Typer =>
966966
def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] =
967967
alts filter (isApplicable(_, argTypes, resultType))
968968

969+
/** Is `alt` a method or polytype whose result type after the first value parameter
970+
* section conforms to the expected type `resultType`? If `resultType`
971+
* is a `IgnoredProto`, pick the underlying type instead.
972+
*/
973+
def resultConforms(alt: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match {
974+
case IgnoredProto(ignored) => resultConforms(alt, ignored)
975+
case _: ValueType =>
976+
alt.widen match {
977+
case tp: PolyType => resultConforms(constrained(tp).resultType, resultType)
978+
case tp: MethodType => constrainResult(tp.resultType, resultType)
979+
case _ => true
980+
}
981+
case _ => true
982+
}
983+
984+
/** If the `chosen` alternative has a result type incompatible with the expected result
985+
* type `pt`, run overloading resolution again on all alternatives that do match `pt`.
986+
* If the latter succeeds with a single alternative, return it, otherwise
987+
* fallback to `chosen`.
988+
*
989+
* Note this order of events is done for speed. One might be tempted to
990+
* preselect alternatives by result type. But is slower, because it discriminates
991+
* less. The idea is when searching for a best solution, as is the case in overloading
992+
* resolution, we should first try criteria which are cheap and which have a high
993+
* probability of pruning the search. result type comparisons are neither cheap nor
994+
* do they prune much, on average.
995+
*/
996+
def adaptByResult(alts: List[TermRef], chosen: TermRef) =
997+
if (ctx.isAfterTyper) chosen
998+
else {
999+
def nestedCtx = ctx.fresh.setExploreTyperState
1000+
pt match {
1001+
case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) =>
1002+
alts.filter(alt =>
1003+
(alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match {
1004+
case Nil => chosen
1005+
case alt2 :: Nil => alt2
1006+
case alts2 =>
1007+
resolveOverloaded(alts2, pt) match {
1008+
case alt2 :: Nil => alt2
1009+
case _ => chosen
1010+
}
1011+
}
1012+
case _ => chosen
1013+
}
1014+
}
1015+
9691016
val candidates = pt match {
9701017
case pt @ FunProto(args, resultType, _) =>
9711018
val numArgs = args.length
@@ -1029,56 +1076,16 @@ trait Applications extends Compatibility { self: Typer =>
10291076
}
10301077
if (isDetermined(candidates)) candidates
10311078
else narrowMostSpecific(candidates) match {
1032-
case result @ (alt1 :: alt2 :: _) =>
1033-
// overload.println(i"ambiguous $alt1 $alt2")
1079+
case Nil => Nil
1080+
case alt :: Nil => adaptByResult(candidates, alt) :: Nil
1081+
case alts =>
1082+
// overload.println(i"ambiguous $alts%, %")
10341083
val deepPt = pt.deepenProto
1035-
if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs)
1036-
else result
1037-
case result =>
1038-
result
1084+
if (deepPt ne pt) resolveOverloaded(candidates, deepPt, targs)
1085+
else alts
10391086
}
10401087
}
10411088

1042-
/** If the `chosen` alternative has a result type incompatible with the expected result
1043-
* type `pt`, run overloading resolution again on all alternatives that do match `pt`.
1044-
* If the latter succeeds with a single alternative, return it, otherwise
1045-
* fallback to `chosen`.
1046-
*/
1047-
def adaptByResult(alts: List[TermRef], chosen: TermRef, pt: Type)(implicit ctx: Context) =
1048-
if (ctx.isAfterTyper) chosen
1049-
else {
1050-
def nestedCtx = ctx.fresh.setExploreTyperState
1051-
pt match {
1052-
case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) =>
1053-
alts.filter(alt =>
1054-
(alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match {
1055-
case Nil => chosen
1056-
case alt2 :: Nil => alt2
1057-
case alts2 =>
1058-
resolveOverloaded(alts2, pt) match {
1059-
case alt2 :: Nil => alt2
1060-
case _ => chosen
1061-
}
1062-
}
1063-
case _ => chosen
1064-
}
1065-
}
1066-
1067-
/** Is `alt` a method or polytype whose result type after the first value parameter
1068-
* section conforms to the expected type `resultType`? If `resultType`
1069-
* is a `IgnoredProto`, pick the underlying type instead.
1070-
*/
1071-
private def resultConforms(alt: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match {
1072-
case IgnoredProto(ignored) => resultConforms(alt, ignored)
1073-
case _: ValueType =>
1074-
alt.widen match {
1075-
case tp: PolyType => resultConforms(constrained(tp).resultType, resultType)
1076-
case tp: MethodType => constrainResult(tp.resultType, resultType)
1077-
case _ => true
1078-
}
1079-
case _ => true
1080-
}
1081-
10821089
private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = {
10831090
def numericClasses(ts: List[T], acc: Set[Symbol]): Set[Symbol] = ts match {
10841091
case t :: ts1 =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1238,7 +1238,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
12381238
def expectedStr = err.expectedTypeStr(pt)
12391239
resolveOverloaded(alts, pt) match {
12401240
case alt :: Nil =>
1241-
adapt(tree.withType(adaptByResult(alts, alt, pt)), pt, original)
1241+
adapt(tree.withType(alt), pt, original)
12421242
case Nil =>
12431243
def noMatches =
12441244
errorTree(tree,

0 commit comments

Comments
 (0)