@@ -22,7 +22,7 @@ import ProtoTypes.*
22
22
import Inferencing .*
23
23
import reporting .*
24
24
import Nullables .* , NullOpsDecorator .*
25
- import config .Feature
25
+ import config .{ Feature , SourceVersion }
26
26
27
27
import collection .mutable
28
28
import config .Printers .{overload , typr , unapp }
@@ -1709,6 +1709,12 @@ trait Applications extends Compatibility {
1709
1709
/** Compare two alternatives of an overloaded call or an implicit search.
1710
1710
*
1711
1711
* @param alt1, alt2 Non-overloaded references indicating the two choices
1712
+ * @param preferGeneral When comparing two value types, prefer the more general one
1713
+ * over the more specific one iff `preferGeneral` is true.
1714
+ * `preferGeneral` is set to `true` when we compare two given values, since
1715
+ * then we want the most general evidence that matches the target
1716
+ * type. It is set to `false` for overloading resolution, when we want the
1717
+ * most specific type instead.
1712
1718
* @return 1 if 1st alternative is preferred over 2nd
1713
1719
* -1 if 2nd alternative is preferred over 1st
1714
1720
* 0 if neither alternative is preferred over the other
@@ -1724,27 +1730,26 @@ trait Applications extends Compatibility {
1724
1730
* an alternative that takes more implicit parameters wins over one
1725
1731
* that takes fewer.
1726
1732
*/
1727
- def compare (alt1 : TermRef , alt2 : TermRef )(using Context ): Int = trace(i " compare( $alt1, $alt2) " , overload) {
1733
+ def compare (alt1 : TermRef , alt2 : TermRef , preferGeneral : Boolean = false )(using Context ): Int = trace(i " compare( $alt1, $alt2) " , overload) {
1728
1734
record(" resolveOverloaded.compare" )
1729
1735
1730
- /** Is alternative `alt1` with type `tp1` as specific as alternative
1736
+ /** Is alternative `alt1` with type `tp1` as good as alternative
1731
1737
* `alt2` with type `tp2` ?
1732
1738
*
1733
- * 1. A method `alt1` of type `(p1: T1, ..., pn: Tn)U` is as specific as `alt2`
1739
+ * 1. A method `alt1` of type `(p1: T1, ..., pn: Tn)U` is as good as `alt2`
1734
1740
* if `alt1` is nullary or `alt2` is applicable to arguments (p1, ..., pn) of
1735
1741
* types T1,...,Tn. If the last parameter `pn` has a vararg type T*, then
1736
1742
* `alt1` must be applicable to arbitrary numbers of `T` parameters (which
1737
1743
* implies that it must be a varargs method as well).
1738
1744
* 2. A polymorphic member of type [a1 >: L1 <: U1, ..., an >: Ln <: Un]T is as
1739
- * specific as `alt2` of type `tp2` if T is as specific as `tp2` under the
1745
+ * good as `alt2` of type `tp2` if T is as good as `tp2` under the
1740
1746
* assumption that for i = 1,...,n each ai is an abstract type name bounded
1741
1747
* from below by Li and from above by Ui.
1742
1748
* 3. A member of any other type `tp1` is:
1743
- * a. always as specific as a method or a polymorphic method.
1744
- * b. as specific as a member of any other type `tp2` if `tp1` is compatible
1745
- * with `tp2`.
1749
+ * a. always as good as a method or a polymorphic method.
1750
+ * b. as good as a member of any other type `tp2` if `asGoodValueType(tp1, tp2) = true`
1746
1751
*/
1747
- def isAsSpecific (alt1 : TermRef , tp1 : Type , alt2 : TermRef , tp2 : Type ): Boolean = trace(i " isAsSpecific $tp1 $tp2" , overload) {
1752
+ def isAsGood (alt1 : TermRef , tp1 : Type , alt2 : TermRef , tp2 : Type ): Boolean = trace(i " isAsSpecific $tp1 $tp2" , overload) {
1748
1753
tp1 match
1749
1754
case tp1 : MethodType => // (1)
1750
1755
tp1.paramInfos.isEmpty && tp2.isInstanceOf [LambdaType ]
@@ -1766,69 +1771,94 @@ trait Applications extends Compatibility {
1766
1771
fullyDefinedType(tp1Params, " type parameters of alternative" , alt1.symbol.srcPos)
1767
1772
1768
1773
val tparams = newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags , tp1.instantiateParamInfos(_))
1769
- isAsSpecific (alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2)
1774
+ isAsGood (alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2)
1770
1775
}
1771
1776
case _ => // (3)
1777
+ def isGiven (alt : TermRef ) =
1778
+ alt1.symbol.is(Given ) && alt.symbol != defn.NotGivenClass
1779
+ def compareValues (tp1 : Type , tp2 : Type )(using Context ) =
1780
+ isAsGoodValueType(tp1, tp2, isGiven(alt1), isGiven(alt2))
1772
1781
tp2 match
1773
1782
case tp2 : MethodType => true // (3a)
1774
1783
case tp2 : PolyType if tp2.resultType.isInstanceOf [MethodType ] => true // (3a)
1775
1784
case tp2 : PolyType => // (3b)
1776
- explore(isAsSpecificValueType (tp1, instantiateWithTypeVars(tp2)))
1785
+ explore(compareValues (tp1, instantiateWithTypeVars(tp2)))
1777
1786
case _ => // 3b)
1778
- isAsSpecificValueType (tp1, tp2)
1787
+ compareValues (tp1, tp2)
1779
1788
}
1780
1789
1781
- /** Test whether value type `tp1` is as specific as value type `tp2`.
1782
- * Let's abbreviate this to `tp1 <:s tp2`.
1783
- * Previously, `<:s` was the same as `<:`. This behavior is still
1784
- * available under mode `Mode.OldOverloadingResolution`. The new behavior
1785
- * is different, however. Here, `T <:s U` iff
1790
+ /** Test whether value type `tp1` is as good as value type `tp2`.
1791
+ * Let's abbreviate this to `tp1 <:p tp2`. The behavior depends on the Scala version
1792
+ * and mode.
1786
1793
*
1787
- * flip(T) <: flip(U)
1794
+ * - In Scala 2, `<:p` was the same as `<:`. This behavior is still
1795
+ * available in 3.0-migration if mode `Mode.OldImplicitResolution` is turned on as well.
1796
+ * It is used to highlight differences between Scala 2 and 3 behavior.
1788
1797
*
1789
- * where `flip` changes covariant occurrences of contravariant type parameters to
1790
- * covariant ones. Intuitively `<:s` means subtyping `<:`, except that all arguments
1791
- * to contravariant parameters are compared as if they were covariant. E.g. given class
1798
+ * - In Scala 3.0-3.5, the behavior is as follows: `T <:p U` iff there is an impliit conversion
1799
+ * from `T` to `U`, or
1792
1800
*
1793
- * class Cmp[-X]
1801
+ * flip(T) <: flip(U)
1794
1802
*
1795
- * `Cmp[T] <:s Cmp[U]` if `T <: U`. On the other hand, non-variant occurrences
1796
- * of parameters are not affected. So `T <: U` would imply `Set[Cmp[U]] <:s Set[Cmp[T]]`,
1797
- * as usual, because `Set` is non-variant.
1803
+ * where `flip` changes covariant occurrences of contravariant type parameters to
1804
+ * covariant ones. Intuitively `<:p` means subtyping `<:`, except that all arguments
1805
+ * to contravariant parameters are compared as if they were covariant. E.g. given class
1798
1806
*
1799
- * This relation might seem strange, but it models closely what happens for methods.
1800
- * Indeed, if we integrate the existing rules for methods into `<:s` we have now that
1807
+ * class Cmp[-X]
1801
1808
*
1802
- * (T)R <:s (U)R
1809
+ * `Cmp[T] <:p Cmp[U]` if `T <: U`. On the other hand, non-variant occurrences
1810
+ * of parameters are not affected. So `T <: U` would imply `Set[Cmp[U]] <:p Set[Cmp[T]]`,
1811
+ * as usual, because `Set` is non-variant.
1803
1812
*
1804
- * iff
1813
+ * - From Scala 3.6, `T <:p U` means `T <: U` or `T` convertible to `U`
1814
+ * for overloading resolution (when `preferGeneral is false), and the opposite relation
1815
+ * `U <: T` or `U convertible to `T` for implicit disambiguation between givens
1816
+ * (when `preferGeneral` is true). For old-style implicit values, the 3.4 behavior is kept.
1817
+ * If one of the alternatives is a given and the other is an implicit, the given wins.
1805
1818
*
1806
- * T => R <:s U => R
1819
+ * - In Scala 3.5 and Scala 3.6-migration, we issue a warning if the result under
1820
+ * Scala 3.6 differ wrt to the old behavior up to 3.5.
1807
1821
*
1808
- * Also: If a compared type refers to a given or its module class, use
1822
+ * Also and only for given resolution : If a compared type refers to a given or its module class, use
1809
1823
* the intersection of its parent classes instead.
1810
1824
*/
1811
- def isAsSpecificValueType (tp1 : Type , tp2 : Type )(using Context ) =
1812
- if (ctx.mode.is(Mode .OldOverloadingResolution ))
1825
+ def isAsGoodValueType (tp1 : Type , tp2 : Type , alt1isGiven : Boolean , alt2isGiven : Boolean )(using Context ): Boolean =
1826
+ val oldResolution = ctx.mode.is(Mode .OldImplicitResolution )
1827
+ if ! preferGeneral || Feature .migrateTo3 && oldResolution then
1828
+ // Normal specificity test for overloading resolution (where `preferGeneral` is false)
1829
+ // and in mode Scala3-migration when we compare with the old Scala 2 rules.
1813
1830
isCompatible(tp1, tp2)
1814
- else {
1815
- val flip = new TypeMap {
1816
- def apply (t : Type ) = t match {
1817
- case t @ AppliedType (tycon, args) =>
1818
- def mapArg (arg : Type , tparam : TypeParamInfo ) =
1819
- if (variance > 0 && tparam.paramVarianceSign < 0 ) defn.FunctionNOf (arg :: Nil , defn.UnitType )
1820
- else arg
1821
- mapOver(t.derivedAppliedType(tycon, args.zipWithConserve(tycon.typeParams)(mapArg)))
1822
- case _ => mapOver(t)
1823
- }
1824
- }
1825
- def prepare (tp : Type ) = tp.stripTypeVar match {
1831
+ else
1832
+ def prepare (tp : Type ) = tp.stripTypeVar match
1826
1833
case tp : NamedType if tp.symbol.is(Module ) && tp.symbol.sourceModule.is(Given ) =>
1827
- flip(tp.widen.widenToParents)
1828
- case _ => flip(tp)
1829
- }
1830
- (prepare(tp1) relaxed_<:< prepare(tp2)) || viewExists(tp1, tp2)
1831
- }
1834
+ tp.widen.widenToParents
1835
+ case _ =>
1836
+ tp
1837
+
1838
+ val tp1p = prepare(tp1)
1839
+ val tp2p = prepare(tp2)
1840
+
1841
+ if Feature .sourceVersion.isAtMost(SourceVersion .`3.4`)
1842
+ || oldResolution
1843
+ || ! alt1isGiven && ! alt2isGiven
1844
+ then
1845
+ // Intermediate rules: better means specialize, but map all type arguments downwards
1846
+ // These are enabled for 3.0-3.5, and for all comparisons between old-style implicits,
1847
+ // and in 3.5 amd 3.6-migration when we compare with previous rules.
1848
+ val flip = new TypeMap :
1849
+ def apply (t : Type ) = t match
1850
+ case t @ AppliedType (tycon, args) =>
1851
+ def mapArg (arg : Type , tparam : TypeParamInfo ) =
1852
+ if (variance > 0 && tparam.paramVarianceSign < 0 ) defn.FunctionNOf (arg :: Nil , defn.UnitType )
1853
+ else arg
1854
+ mapOver(t.derivedAppliedType(tycon, args.zipWithConserve(tycon.typeParams)(mapArg)))
1855
+ case _ => mapOver(t)
1856
+ (flip(tp1p) relaxed_<:< flip(tp2p)) || viewExists(tp1, tp2)
1857
+ else
1858
+ // New rules: better means generalize, givens always beat implicits
1859
+ if alt1isGiven != alt2isGiven then alt1isGiven
1860
+ else (tp2p relaxed_<:< tp1p) || viewExists(tp2, tp1)
1861
+ end isAsGoodValueType
1832
1862
1833
1863
/** Widen the result type of synthetic given methods from the implementation class to the
1834
1864
* type that's implemented. Example
@@ -1880,17 +1910,16 @@ trait Applications extends Compatibility {
1880
1910
def comparePrefixes =
1881
1911
val pre1 = widenPrefix(alt1)
1882
1912
val pre2 = widenPrefix(alt2)
1883
- val winsPrefix1 = isAsSpecificValueType (pre1, pre2)
1884
- val winsPrefix2 = isAsSpecificValueType (pre2, pre1)
1913
+ val winsPrefix1 = isCompatible (pre1, pre2)
1914
+ val winsPrefix2 = isCompatible (pre2, pre1)
1885
1915
if winsPrefix1 == winsPrefix2 then 0
1886
1916
else if winsPrefix1 then 1
1887
1917
else - 1
1888
1918
1889
1919
def compareWithTypes (tp1 : Type , tp2 : Type ) =
1890
1920
val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner)
1891
-
1892
- val winsType1 = isAsSpecific(alt1, tp1, alt2, tp2)
1893
- val winsType2 = isAsSpecific(alt2, tp2, alt1, tp1)
1921
+ val winsType1 = isAsGood(alt1, tp1, alt2, tp2)
1922
+ val winsType2 = isAsGood(alt2, tp2, alt1, tp1)
1894
1923
1895
1924
overload.println(i " compare( $alt1, $alt2)? $tp1 $tp2 $ownerScore $winsType1 $winsType2" )
1896
1925
if winsType1 && winsType2
0 commit comments