Skip to content

Commit 54835b6

Browse files
committed
Take expected result type into account more often for overloading resolution
Previously, the expected result type of a FunProto type was ignored and taken into account only in case of ambiguities. arrayclone-new.scala shows that this is not enough. In a case like val x: Array[Byte] = Array(1, 2) we typed 1, 2 to be Int, so overloading resulution would give the Array.apply of type (Int, Int*)Array[Int]. But that's a dead end, since Array[Int] is not a subtype of Array[Byte]. This commit proposes the following modified rule for overloading resulution: A method alternative is applicable if ... (as before), and if its result type is copmpatible with the expected type of the method application. The commit does not pre-select alternatives based on comparing with the expected result type. I tried that but it slowed down typechecking by a factor of at least 4. Instead, we proceed as usual, ignoring the result type except in case of ambiguities, but check whether the result of overloading resolution has a compatible result type. If that's not the case, we filter all alternatives for result type compatibility and try again.
1 parent 1b31f06 commit 54835b6

File tree

4 files changed

+44
-3
lines changed

4 files changed

+44
-3
lines changed

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

+40
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,46 @@ trait Applications extends Compatibility { self: Typer =>
10391039
}
10401040
}
10411041

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+
10421082
private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = {
10431083
def numericClasses(ts: List[T], acc: Set[Symbol]): Set[Symbol] = ts match {
10441084
case t :: ts1 =>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ object ProtoTypes {
4646
* fits the given expected result type.
4747
*/
4848
def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match {
49-
case _: FunProto =>
49+
case pt: FunProto =>
5050
mt match {
5151
case mt: MethodType =>
5252
mt.isDependent || constrainResult(mt.resultType, pt.resultType)

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

+1-1
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(alt), pt, original)
1241+
adapt(tree.withType(adaptByResult(alts, alt, pt)), pt, original)
12421242
case Nil =>
12431243
def noMatches =
12441244
errorTree(tree,

tests/pending/run/arrayclone-new.scala renamed to tests/run/arrayclone-new.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import scala.reflect.{ClassTag, classTag}
1+
import scala.reflect.ClassTag
22

33
object Test extends dotty.runtime.LegacyApp{
44
BooleanArrayClone;
@@ -106,3 +106,4 @@ object PolymorphicArrayClone{
106106

107107
testIt(mangled.it, 0, 1);
108108
}
109+

0 commit comments

Comments
 (0)