@@ -1615,11 +1615,12 @@ trait Applications extends Compatibility {
1615
1615
* Module classes also inherit the relationship from their companions. This means,
1616
1616
* if no direct derivation exists between `sym1` and `sym2` also perform the following
1617
1617
* tests:
1618
- * - If both sym1 and sym1 are module classes that have companion classes,
1619
- * and sym2 does not inherit implicit members from a base class (#),
1620
- * compare the companion classes.
1621
- * - If sym1 is a module class with a companion, and sym2 is a normal class or trait,
1622
- * compare the companion with sym2.
1618
+ * - If both sym1 and sym2 are module classes that have companion classes,
1619
+ * compare the companion classes. Return the result of that comparison,
1620
+ * provided the module class with the larger companion class does not itself
1621
+ * inherit implicit members from a base class (#),
1622
+ * - If one sym is a module class with a companion, and the other is a normal class or trait,
1623
+ * compare the companion with the other class or trait.
1623
1624
*
1624
1625
* Condition (#) is necessary to make `compareOwner(_, _) > 0` a transitive relation.
1625
1626
* For instance:
@@ -1643,17 +1644,22 @@ trait Applications extends Compatibility {
1643
1644
* This means we get an ambiguity between `a` and `b` in all cases.
1644
1645
*/
1645
1646
def compareOwner (sym1 : Symbol , sym2 : Symbol )(using Context ): Int =
1647
+ def cls1 = sym1.companionClass
1648
+ def cls2 = sym2.companionClass
1646
1649
if sym1 == sym2 then 0
1647
1650
else if sym1.isSubClass(sym2) then 1
1648
1651
else if sym2.isSubClass(sym1) then - 1
1649
- else if sym1.is(Module ) then
1650
- val cls1 = sym1.companionClass
1651
- if sym2.is(Module ) then
1652
- if sym2.thisType.implicitMembers.forall(_.symbol.owner == sym2) then // test for (#)
1653
- compareOwner(cls1, sym2.companionClass)
1654
- else 0
1655
- else compareOwner(cls1, sym2)
1656
- else 0
1652
+ else
1653
+ if sym1.is(Module ) && sym2.is(Module ) then
1654
+ val r = compareOwner(cls1, cls2)
1655
+ if r == 0 then 0
1656
+ else
1657
+ val larger = if r < 0 then sym1 else sym2
1658
+ if larger.thisType.implicitMembers.forall(_.symbol.owner == larger) then r
1659
+ else 0
1660
+ else if sym1.is(Module ) then compareOwner(cls1, sym2)
1661
+ else if sym2.is(Module ) then compareOwner(sym1, cls2)
1662
+ else 0
1657
1663
1658
1664
/** Compare two alternatives of an overloaded call or an implicit search.
1659
1665
*
@@ -1808,10 +1814,38 @@ trait Applications extends Compatibility {
1808
1814
else tp
1809
1815
}
1810
1816
1811
- def compareWithTypes (tp1 : Type , tp2 : Type ) = {
1817
+ def widenPrefix (alt : TermRef ): Type = alt.prefix.widen match
1818
+ case pre : (TypeRef | ThisType ) if pre.typeSymbol.is(Module ) =>
1819
+ pre.parents.reduceLeft(TypeComparer .andType(_, _))
1820
+ case wpre => wpre
1821
+
1822
+ /** If two alternatives have the same symbol, we pick the one with the most
1823
+ * specific prefix. To determine that, we widen the prefix types and also
1824
+ * widen module classes to the intersection of their parent classes. Then
1825
+ * if one of the resulting types is a more specific value type than the other,
1826
+ * it wins. Example:
1827
+ *
1828
+ * trait A { given M = ... }
1829
+ * trait B extends A
1830
+ * object a extends A
1831
+ * object b extends B
1832
+ *
1833
+ * In this case `b.M` would be regarded as more specific than `a.M`.
1834
+ */
1835
+ def comparePrefixes =
1836
+ val pre1 = widenPrefix(alt1)
1837
+ val pre2 = widenPrefix(alt2)
1838
+ val winsPrefix1 = isAsSpecificValueType(pre1, pre2)
1839
+ val winsPrefix2 = isAsSpecificValueType(pre2, pre1)
1840
+ if winsPrefix1 == winsPrefix2 then 0
1841
+ else if winsPrefix1 then 1
1842
+ else - 1
1843
+
1844
+ def compareWithTypes (tp1 : Type , tp2 : Type ) =
1812
1845
val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner)
1813
- def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2)
1814
- def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1)
1846
+
1847
+ val winsType1 = isAsSpecific(alt1, tp1, alt2, tp2)
1848
+ val winsType2 = isAsSpecific(alt2, tp2, alt1, tp1)
1815
1849
1816
1850
overload.println(i " compare( $alt1, $alt2)? $tp1 $tp2 $ownerScore $winsType1 $winsType2" )
1817
1851
if winsType1 && winsType2
@@ -1820,15 +1854,14 @@ trait Applications extends Compatibility {
1820
1854
// alternatives are the same after following ExprTypes, pick one of them
1821
1855
// (prefer the one that is not a method, but that's arbitrary).
1822
1856
if alt1.widenExpr =:= alt2 then - 1 else 1
1823
- else if ownerScore == 1 then
1824
- if winsType1 || ! winsType2 then 1 else 0
1825
- else if ownerScore == - 1 then
1826
- if winsType2 || ! winsType1 then - 1 else 0
1827
- else if winsType1 then
1828
- if winsType2 then 0 else 1
1829
- else
1830
- if winsType2 then - 1 else 0
1831
- }
1857
+ else ownerScore match
1858
+ case 1 => if winsType1 || ! winsType2 then 1 else 0
1859
+ case - 1 => if winsType2 || ! winsType1 then - 1 else 0
1860
+ case 0 =>
1861
+ if winsType1 != winsType2 then if winsType1 then 1 else - 1
1862
+ else if alt1.symbol == alt2.symbol then comparePrefixes
1863
+ else 0
1864
+ end compareWithTypes
1832
1865
1833
1866
if alt1.symbol.is(ConstructorProxy ) && ! alt2.symbol.is(ConstructorProxy ) then - 1
1834
1867
else if alt2.symbol.is(ConstructorProxy ) && ! alt1.symbol.is(ConstructorProxy ) then 1
0 commit comments