Skip to content

Commit

Permalink
Refine overloading and implicit disambiguation
Browse files Browse the repository at this point in the history
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 scala#20054.
  • Loading branch information
odersky committed Apr 3, 2024
1 parent c8c3bde commit 205272c
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
32 changes: 31 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
14 changes: 14 additions & 0 deletions tests/pos/implicit-prefix-disambiguation.scala
Original file line number Diff line number Diff line change
@@ -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]]


0 comments on commit 205272c

Please sign in to comment.