Skip to content

Commit 8524295

Browse files
committed
Fix #9058: Make implicit search preference relation transitive
1 parent 70bf21b commit 8524295

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,14 +1347,18 @@ trait Applications extends Compatibility {
13471347
* @return 1 if `sym1` properly derives from `sym2`
13481348
* -1 if `sym2` properly derives from `sym1`
13491349
* 0 otherwise
1350-
* Module classes also inherit the relationship from their companions.
1350+
* Module classes inherit the relationship from their companions. This means:
1351+
* - If both sym1 and sym1 are module classes that have companion classes, compare the companions
1352+
* - If sym1 is a module class with a companion, and sym2 is a normal class or trait, compare
1353+
* the companion with sym2.
1354+
* Going beyond that risks making compareOwner non-transitive.
13511355
*/
13521356
def compareOwner(sym1: Symbol, sym2: Symbol)(using Context): Int =
13531357
if sym1 == sym2 then 0
1358+
else if sym1.is(Module) && sym1.companionClass.exists then
1359+
compareOwner(sym1.companionClass, if sym2.is(Module) then sym2.companionClass else sym2)
13541360
else if sym1.isSubClass(sym2) then 1
13551361
else if sym2.isSubClass(sym1) then -1
1356-
else if sym1.is(Module) && sym2.is(Module) then
1357-
compareOwner(sym1.companionClass, sym2.companionClass)
13581362
else 0
13591363

13601364
/** Compare to alternatives of an overloaded call or an implicit search.

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,13 +1169,13 @@ trait Implicits { self: Typer =>
11691169
if (level1 < level2) return false
11701170
val sym1 = cand1.ref.symbol
11711171
val sym2 = cand2.ref.symbol
1172-
val ownerScore = compareOwner(sym1.maybeOwner, sym2.maybeOwner)
1173-
if (ownerScore > 0) return true
1174-
if (ownerScore < 0) return false
11751172
val arity1 = sym1.info.firstParamTypes.length
11761173
val arity2 = sym2.info.firstParamTypes.length
11771174
if (arity1 < arity2) return true
11781175
if (arity1 > arity2) return false
1176+
val ownerScore = compareOwner(sym1.maybeOwner, sym2.maybeOwner)
1177+
if (ownerScore > 0) return true
1178+
if (ownerScore < 0) return false
11791179
false
11801180
}
11811181

@@ -1190,7 +1190,19 @@ trait Implicits { self: Typer =>
11901190
if (prefer(e2, e1)) e2 :: e1 :: Nil
11911191
else eligible
11921192
case _ =>
1193-
eligible.sortWith(prefer)
1193+
try eligible.sortWith(prefer)
1194+
catch case ex: IllegalArgumentException =>
1195+
// diagnostic to see what went wrong
1196+
for
1197+
e1 <- eligible
1198+
e2 <- eligible
1199+
if prefer(e1, e2)
1200+
e3 <- eligible
1201+
if prefer(e2, e3) && !prefer(e1, e3)
1202+
do
1203+
val es = List(e1, e2, e3)
1204+
println(i"transitivity violated for $es%, %\n ${es.map(_.implicitRef.underlyingRef.symbol.showLocated)}%, %")
1205+
throw ex
11941206
}
11951207

11961208
rank(sort(eligible), NoMatchingImplicitsFailure, Nil)

0 commit comments

Comments
 (0)