From 946401c8f2937d0c8d61a60740acb470f598f636 Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Sun, 31 Mar 2024 12:50:04 +0200 Subject: [PATCH] Improve overload resolution `resolveOverloaded1` starts with `narrowMostSpecific(candidates)` which compares the alternatives based on the 1st argument list. If more than one result if found, we can continue with the 2nd argument list. But we do so with the original candidates, rather than with the result of `narrowMostSpecific`. This can lead to choosing an alternative which is not the most specific, by now disregarding the 1st argument list. In i120053, the 1st pass correctly eliminated the 3rd `def ^^^`, but could not resolve between the 1st two (having the same argument list). The 2nd pass then disregarded this and restarted the comparison based on the 2nd argument list alone, which incorrectly yielded the 3rd option. The change is simply using the initial result of `narrowMostSpecific` in the recursive resolution based on subsequent argument lists. I'm not sure however if the same changes should apply to the rest of the cases attempting further narrowing ? --- .../dotty/tools/dotc/typer/Applications.scala | 4 +- .../test/dotc/pos-test-pickling.blacklist | 1 + tests/neg/i10901.check | 36 --------- tests/neg/i10901.scala | 4 +- tests/pos/i20053.scala | 79 +++++++++++++++++++ 5 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 tests/pos/i20053.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 82f4c89ae203..34b4a2b64448 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -2184,11 +2184,11 @@ trait Applications extends Compatibility { deepPt match case pt @ FunProto(_, PolyProto(targs, resType)) => // try to narrow further with snd argument list and following type params - resolveMapped(candidates, + resolveMapped(found, skipParamClause(pt.typedArgs().tpes, targs.tpes), resType) case pt @ FunProto(_, resType: FunOrPolyProto) => // try to narrow further with snd argument list - resolveMapped(candidates, + resolveMapped(found, skipParamClause(pt.typedArgs().tpes, Nil), resType) case _ => // prefer alternatives that need no eta expansion diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 3785f8fa6e06..605c98742dfd 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -117,4 +117,5 @@ i7445b.scala # more aggresive reduce projection makes a difference i15525.scala +i20053.scala diff --git a/tests/neg/i10901.check b/tests/neg/i10901.check index 4a8fa5db28bf..45e2e7f763dd 100644 --- a/tests/neg/i10901.check +++ b/tests/neg/i10901.check @@ -1,39 +1,3 @@ --- [E008] Not Found Error: tests/neg/i10901.scala:45:38 ---------------------------------------------------------------- -45 | val pos1: Point2D[Int,Double] = x º y // error - | ^^^ - | value º is not a member of object BugExp4Point2D.IntT. - | An extension method was tried, but could not be fully constructed: - | - | º(x) - | - | failed with: - | - | Ambiguous overload. The overloaded alternatives of method º in object dsl with types - | [T1, T2] - | (x: BugExp4Point2D.ColumnType[T1]) - | (y: BugExp4Point2D.ColumnType[T2]) - | (implicit evidence$1: Numeric[T1], evidence$2: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] - | [T1, T2] - | (x: T1) - | (y: BugExp4Point2D.ColumnType[T2]) - | (implicit evidence$1: Numeric[T1], evidence$2: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] - | both match arguments ((x : BugExp4Point2D.IntT.type))((y : BugExp4Point2D.DoubleT.type)) --- [E008] Not Found Error: tests/neg/i10901.scala:48:38 ---------------------------------------------------------------- -48 | val pos4: Point2D[Int,Double] = x º 201.1 // error - | ^^^ - |value º is not a member of object BugExp4Point2D.IntT. - |An extension method was tried, but could not be fully constructed: - | - | º(x) - | - | failed with: - | - | Ambiguous overload. The overloaded alternatives of method º in object dsl with types - | [T1, T2] - | (x: BugExp4Point2D.ColumnType[T1]) - | (y: T2)(implicit evidence$1: Numeric[T1], evidence$2: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] - | [T1, T2](x: T1)(y: T2)(implicit evidence$1: Numeric[T1], evidence$2: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] - | both match arguments ((x : BugExp4Point2D.IntT.type))((201.1d : Double)) -- [E008] Not Found Error: tests/neg/i10901.scala:62:16 ---------------------------------------------------------------- 62 | val y = "abc".foo // error | ^^^^^^^^^ diff --git a/tests/neg/i10901.scala b/tests/neg/i10901.scala index dc1ea6e6eef6..ed6bb3f90604 100644 --- a/tests/neg/i10901.scala +++ b/tests/neg/i10901.scala @@ -42,10 +42,10 @@ object BugExp4Point2D { val x = IntT val y = DoubleT - val pos1: Point2D[Int,Double] = x º y // error + val pos1: Point2D[Int,Double] = x º y // ok val pos2: Point2D[Int,Double] = 100 º 200.1 // ok val pos3: Point2D[Int,Double] = 101 º y // ok - val pos4: Point2D[Int,Double] = x º 201.1 // error + val pos4: Point2D[Int,Double] = x º 201.1 // ok } } diff --git a/tests/pos/i20053.scala b/tests/pos/i20053.scala new file mode 100644 index 000000000000..6c0a3390e0bb --- /dev/null +++ b/tests/pos/i20053.scala @@ -0,0 +1,79 @@ + +trait Summon[R, T <: R]: + type Out +object Summon: + given [R, T <: R]: Summon[R, T] with + type Out = R + +sealed class Modifier[+A, +P] +type ModifierAny = Modifier[Any, Any] +sealed trait ISCONST[T <: Boolean] +type CONST = ISCONST[true] + +trait DFTypeAny +trait DFBits[W <: Int] extends DFTypeAny +trait DFVal[+T <: DFTypeAny, +M <: ModifierAny] +type DFValAny = DFVal[DFTypeAny, ModifierAny] +type DFValTP[+T <: DFTypeAny, +P] = DFVal[T, Modifier[Any, P]] +type DFConstOf[+T <: DFTypeAny] = DFVal[T, Modifier[Any, CONST]] + +trait Candidate[R]: + type OutW <: Int + type OutP +object Candidate: + given [W <: Int, P, R <: DFValTP[DFBits[W], P]]: Candidate[R] with + type OutW = W + type OutP = P + +extension [L <: DFValAny](lhs: L)(using icL: Candidate[L]) + def ^^^[R](rhs: R)(using + icR: Candidate[R] + ): DFValTP[DFBits[icL.OutW], icL.OutP | icR.OutP] = ??? + def ^^^ : Unit = ??? +extension [L](lhs: L) + def ^^^[RW <: Int, RP]( + rhs: DFValTP[DFBits[RW], RP] + )(using es: Summon[L, lhs.type])(using + c: Candidate[L] + )(using check: c.OutW =:= c.OutW): DFValTP[DFBits[c.OutW], c.OutP | RP] = ??? + +val x: DFConstOf[DFBits[8]] = ??? +val zzz = x ^^^ x ^^^ x + + +object Minimized: + trait DFVal[+T <: Int, +P] + + trait Summon[R, T <: R] + given [R, T <: R]: Summon[R, T] with {} + + trait Candidate[R]: + type OutW <: Int + type OutP + given [W <: Int, P, R <: DFVal[W, P]]: Candidate[R] with + type OutW = W + type OutP = P + + extension [L <: DFVal[Int, Any]](lhs: L)(using icL: Candidate[L]) + def ^^^[R](rhs: R) + (using icR: Candidate[R]) + : DFVal[icL.OutW, icL.OutP | icR.OutP] = ??? + def ^^^ : Unit = ??? + + extension [L](lhs: L) + def ^^^[RW <: Int, RP](rhs: DFVal[RW, RP]) + (using es: Summon[L, lhs.type]) + (using c: Candidate[L]) + (using check: c.OutW =:= c.OutW) + : DFVal[c.OutW, c.OutP | RP] = ??? + + val x: DFVal[8, true] = ??? + val z1 = x ^^^ x // Ok + val z2 = z1 ^^^ x // Ok + val zzz = x ^^^ x ^^^ x // Error before changes + + /* Before the changes, when `def ^^^ : Unit = ???` is present, + * all of z1, z2, zzz attempt to use the last `def ^^^`, + * despite it being less specific than the 1st one. + */ +end Minimized