@@ -1347,20 +1347,53 @@ trait Applications extends Compatibility {
1347
1347
* @return 1 if `sym1` properly derives from `sym2`
1348
1348
* -1 if `sym2` properly derives from `sym1`
1349
1349
* 0 otherwise
1350
- * Module classes inherit the relationship from their companions. This means:
1350
+ * Module classes also inherit the relationship from their companions. This means,
1351
+ * if no direct derivation exists between `sym1` and `sym2` also perform the following
1352
+ * tests:
1351
1353
* - If both sym1 and sym1 are module classes that have companion classes, compare the companions
1352
1354
* - If sym1 is a module class with a companion, and sym2 is a normal class or trait, compare
1353
1355
* the companion with sym2.
1354
- * Going beyond that risks making compareOwner non-transitive.
1356
+ *
1357
+ * Note that this makes `compareOwner(_, _) > 0` not transitive! For instance:
1358
+ *
1359
+ * class A extends B
1360
+ * object A
1361
+ * class B
1362
+ * object B extends C
1363
+ *
1364
+ * Then compareOwner(A$, B$) = 1 and compareOwner(B$, C) == 1, but
1365
+ * compareOwner(A$, C) == 0.
1355
1366
*/
1356
1367
def compareOwner (sym1 : Symbol , sym2 : Symbol )(using Context ): Int =
1357
1368
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)
1360
1369
else if sym1.isSubClass(sym2) then 1
1361
1370
else if sym2.isSubClass(sym1) then - 1
1371
+ else if sym1.is(Module ) then
1372
+ compareOwner(sym1.companionClass, if sym2.is(Module ) then sym2.companionClass else sym2)
1362
1373
else 0
1363
1374
1375
+ /** A version of `compareOwner` that is transitive, to be used in sorting
1376
+ * It would be nice if we could use this as the general method for comparing
1377
+ * owners, but unfortunately this does not compile all existsing code.
1378
+ * An example is `enrich-gentraversable.scala`. Here we have
1379
+ *
1380
+ * trait BuildFrom...
1381
+ * object BuildFrom extends BuildFromLowPriority1
1382
+ *
1383
+ * and we need to pick an implicit in BuildFrom over BuildFromLowPriority1
1384
+ * the rules in `compareOwner` give us that, but the rules in `isSubOwner`
1385
+ * don't.
1386
+ * So we need to stick with `compareOwner` for backwards compatibility, even
1387
+ * though it is arguably broken. We can still use `isSubOwner` for sorting
1388
+ * since that is just a performance optimization, so if the two methods
1389
+ * don't agree sometimes that's OK.
1390
+ */
1391
+ def isSubOwner (sym1 : Symbol , sym2 : Symbol )(using Context ): Boolean =
1392
+ if sym1.is(Module ) && sym1.companionClass.exists then
1393
+ isSubOwner(sym1.companionClass, if sym2.is(Module ) then sym2.companionClass else sym2)
1394
+ else
1395
+ sym1 != sym2 && sym1.isSubClass(sym2)
1396
+
1364
1397
/** Compare to alternatives of an overloaded call or an implicit search.
1365
1398
*
1366
1399
* @param alt1, alt2 Non-overloaded references indicating the two choices
0 commit comments