@@ -1347,16 +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 also inherit the relationship from their companions.
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:
1353
+ * - If both sym1 and sym1 are module classes that have companion classes, compare the companions
1354
+ * - If sym1 is a module class with a companion, and sym2 is a normal class or trait, compare
1355
+ * the companion with sym2.
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.
1351
1366
*/
1352
1367
def compareOwner (sym1 : Symbol , sym2 : Symbol )(using Context ): Int =
1353
- if ( sym1 == sym2) 0
1354
- else if ( sym1 isSubClass sym2) 1
1355
- else if ( sym2 isSubClass sym1) - 1
1356
- else if (sym2 .is(Module )) compareOwner(sym1, sym2.companionClass)
1357
- else if (sym1.is(Module )) compareOwner(sym1 .companionClass, sym2)
1368
+ if sym1 == sym2 then 0
1369
+ else if sym1. isSubClass( sym2) then 1
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)
1358
1373
else 0
1359
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
+
1360
1397
/** Compare to alternatives of an overloaded call or an implicit search.
1361
1398
*
1362
1399
* @param alt1, alt2 Non-overloaded references indicating the two choices
0 commit comments