From 205272ce6b47e890a6f19639847bd1510a130871 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 3 Apr 2024 20:39:07 +0200 Subject: [PATCH] Refine overloading and implicit disambiguation We sometimes have two alternatives a.m and b.m with the same symbol but different prefixes. Previously these would always be ambiguous. We now try to disambiguate this so that the alternative with the more specific prefix wins. To determine this, we widen prefixes also going from module classes to their parents and then compare the resulting types. This might fix a problem in ScalaTest that popped up after #20054. --- .../dotty/tools/dotc/typer/Applications.scala | 32 ++++++++++++++++++- .../pos/implicit-prefix-disambiguation.scala | 14 ++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/pos/implicit-prefix-disambiguation.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 82f4c89ae203..cb119b92431b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1807,8 +1807,38 @@ trait Applications extends Compatibility { else tp } + def widenPrefix(alt: TermRef): Type = alt.prefix.widen match + case pre: (TypeRef | ThisType) if pre.typeSymbol.is(Module) => + pre.parents.reduceLeft(TypeComparer.andType(_, _)) + case wpre => wpre + + /** If two alternatives have the same symbol, we pick the one with the most + * specific prefix. To determine that, we widen the prefix types and also + * widen module classes to the intersection of their parent classes. Then + * if one of the resulting types is a more specific value type than the other, + * it wins. Example: + * + * trait A { given M = ... } + * trait B extends A + * object a extends A + * object b extends B + * + * In this case `b.M` would be regarded as more specific than `a.M`. + */ + def comparePrefixes(pre1: Type, pre2: Type) = + val winsPrefix1 = isAsSpecificValueType(pre1, pre2) + val winsPrefix2 = isAsSpecificValueType(pre2, pre1) + if winsPrefix1 == winsPrefix2 then 0 + else if winsPrefix1 then 1 + else -1 + def compareWithTypes(tp1: Type, tp2: Type) = { - val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner) + val ownerScore = + val sym1 = alt1.symbol + val sym2 = alt2.symbol + if sym1 == sym2 then comparePrefixes(widenPrefix(alt1), widenPrefix(alt2)) + else compareOwner(sym1.maybeOwner, sym2.maybeOwner) + def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2) def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1) diff --git a/tests/pos/implicit-prefix-disambiguation.scala b/tests/pos/implicit-prefix-disambiguation.scala new file mode 100644 index 000000000000..5059aa2db4eb --- /dev/null +++ b/tests/pos/implicit-prefix-disambiguation.scala @@ -0,0 +1,14 @@ +class I[X] + +trait A: + given I[B] = ??? +object A extends A + +trait B extends A +object B extends B + +//import B.given, A.given + +def Test = summon[I[B]] + +