aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/typer/Applications.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty/tools/dotc/typer/Applications.scala')
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala68
1 files changed, 60 insertions, 8 deletions
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 9e9acf97a..c45db4ccc 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -915,7 +915,9 @@ trait Applications extends Compatibility { self: Typer =>
}}
def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {
- (alts: @unchecked) match {
+ alts match {
+ case Nil => alts
+ case _ :: Nil => alts
case alt :: alts1 =>
def winner(bestSoFar: TermRef, alts: List[TermRef]): TermRef = alts match {
case alt :: alts1 =>
@@ -966,6 +968,51 @@ trait Applications extends Compatibility { self: Typer =>
def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] =
alts filter (isApplicable(_, argTypes, resultType))
+ /** Is `alt` a method or polytype whose result type after the first value parameter
+ * section conforms to the expected type `resultType`? If `resultType`
+ * is a `IgnoredProto`, pick the underlying type instead.
+ */
+ def resultConforms(alt: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match {
+ case IgnoredProto(ignored) => resultConforms(alt, ignored)
+ case _: ValueType =>
+ alt.widen match {
+ case tp: PolyType => resultConforms(constrained(tp).resultType, resultType)
+ case tp: MethodType => constrainResult(tp.resultType, resultType)
+ case _ => true
+ }
+ case _ => true
+ }
+
+ /** If the `chosen` alternative has a result type incompatible with the expected result
+ * type `pt`, run overloading resolution again on all alternatives that do match `pt`.
+ * If the latter succeeds with a single alternative, return it, otherwise
+ * fallback to `chosen`.
+ *
+ * Note this order of events is done for speed. One might be tempted to
+ * preselect alternatives by result type. But is slower, because it discriminates
+ * less. The idea is when searching for a best solution, as is the case in overloading
+ * resolution, we should first try criteria which are cheap and which have a high
+ * probability of pruning the search. result type comparisons are neither cheap nor
+ * do they prune much, on average.
+ */
+ def adaptByResult(alts: List[TermRef], chosen: TermRef) = {
+ def nestedCtx = ctx.fresh.setExploreTyperState
+ pt match {
+ case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) =>
+ alts.filter(alt =>
+ (alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match {
+ case Nil => chosen
+ case alt2 :: Nil => alt2
+ case alts2 =>
+ resolveOverloaded(alts2, pt) match {
+ case alt2 :: Nil => alt2
+ case _ => chosen
+ }
+ }
+ case _ => chosen
+ }
+ }
+
val candidates = pt match {
case pt @ FunProto(args, resultType, _) =>
val numArgs = args.length
@@ -1027,15 +1074,20 @@ trait Applications extends Compatibility { self: Typer =>
case pt =>
alts filter (normalizedCompatible(_, pt))
}
- if (isDetermined(candidates)) candidates
- else narrowMostSpecific(candidates) match {
- case result @ (alt1 :: alt2 :: _) =>
-// overload.println(i"ambiguous $alt1 $alt2")
+ narrowMostSpecific(candidates) match {
+ case Nil => Nil
+ case alt :: Nil =>
+ adaptByResult(alts, alt) :: Nil
+ // why `alts` and not `candidates`? pos/array-overload.scala gives a test case.
+ // Here, only the Int-apply is a candidate, but it is not compatible with the result
+ // type. Picking the Byte-apply as the only result-compatible solution then forces
+ // the arguments (which are constants) to be adapted to Byte. If we had picked
+ // `candidates` instead, no solution would have been found.
+ case alts =>
+// overload.println(i"ambiguous $alts%, %")
val deepPt = pt.deepenProto
if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs)
- else result
- case result =>
- result
+ else alts
}
}