@@ -1347,16 +1347,53 @@ 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 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.
13511366 */
13521367 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)
13581373 else 0
13591374
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+
13601397 /** Compare to alternatives of an overloaded call or an implicit search.
13611398 *
13621399 * @param alt1, alt2 Non-overloaded references indicating the two choices
0 commit comments