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]] + +