@@ -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 }
@@ -1622,6 +1622,12 @@ trait Applications extends Compatibility {
1622
1622
/** Compare two alternatives of an overloaded call or an implicit search.
1623
1623
*
1624
1624
* @param alt1, alt2 Non-overloaded references indicating the two choices
1625
+ * @param preferGeneral When comparing two value types, prefer the more general one
1626
+ * over the more specific one iff `preferGeneral` is true.
1627
+ * `preferGeneral` is set to `true` when we compare two given values, since
1628
+ * then we want the most general evidence that matches the target
1629
+ * type. It is set to `false` for overloading resolution, when we want the
1630
+ * most specific type instead.
1625
1631
* @return 1 if 1st alternative is preferred over 2nd
1626
1632
* -1 if 2nd alternative is preferred over 1st
1627
1633
* 0 if neither alternative is preferred over the other
@@ -1640,27 +1646,25 @@ trait Applications extends Compatibility {
1640
1646
def compare (alt1 : TermRef , alt2 : TermRef , preferGeneral : Boolean = false )(using Context ): Int = trace(i " compare( $alt1, $alt2) " , overload) {
1641
1647
record(" resolveOverloaded.compare" )
1642
1648
1643
- val newGivenRules =
1644
- ctx.mode.is(Mode .NewGivenRules ) && alt1.symbol.is(Given )
1649
+ val compareGivens = alt1.symbol.is(Given ) || alt2.symbol.is(Given )
1645
1650
1646
- /** Is alternative `alt1` with type `tp1` as specific as alternative
1651
+ /** Is alternative `alt1` with type `tp1` as good as alternative
1647
1652
* `alt2` with type `tp2` ?
1648
1653
*
1649
- * 1. A method `alt1` of type `(p1: T1, ..., pn: Tn)U` is as specific as `alt2`
1654
+ * 1. A method `alt1` of type `(p1: T1, ..., pn: Tn)U` is as good as `alt2`
1650
1655
* if `alt1` is nullary or `alt2` is applicable to arguments (p1, ..., pn) of
1651
1656
* types T1,...,Tn. If the last parameter `pn` has a vararg type T*, then
1652
1657
* `alt1` must be applicable to arbitrary numbers of `T` parameters (which
1653
1658
* implies that it must be a varargs method as well).
1654
1659
* 2. A polymorphic member of type [a1 >: L1 <: U1, ..., an >: Ln <: Un]T is as
1655
- * specific as `alt2` of type `tp2` if T is as specific as `tp2` under the
1660
+ * good as `alt2` of type `tp2` if T is as good as `tp2` under the
1656
1661
* assumption that for i = 1,...,n each ai is an abstract type name bounded
1657
1662
* from below by Li and from above by Ui.
1658
1663
* 3. A member of any other type `tp1` is:
1659
- * a. always as specific as a method or a polymorphic method.
1660
- * b. as specific as a member of any other type `tp2` if `tp1` is compatible
1661
- * with `tp2`.
1664
+ * a. always as good as a method or a polymorphic method.
1665
+ * b. as good as a member of any other type `tp2` is `asGoodValueType(tp1, tp2) = true`
1662
1666
*/
1663
- def isAsSpecific (alt1 : TermRef , tp1 : Type , alt2 : TermRef , tp2 : Type ): Boolean = trace(i " isAsSpecific $tp1 $tp2" , overload) {
1667
+ def isAsGood (alt1 : TermRef , tp1 : Type , alt2 : TermRef , tp2 : Type ): Boolean = trace(i " isAsSpecific $tp1 $tp2" , overload) {
1664
1668
tp1 match
1665
1669
case tp1 : MethodType => // (1)
1666
1670
tp1.paramInfos.isEmpty && tp2.isInstanceOf [LambdaType ]
@@ -1682,65 +1686,60 @@ trait Applications extends Compatibility {
1682
1686
fullyDefinedType(tp1Params, " type parameters of alternative" , alt1.symbol.srcPos)
1683
1687
1684
1688
val tparams = newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags , tp1.instantiateParamInfos(_))
1685
- isAsSpecific (alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2)
1689
+ isAsGood (alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2)
1686
1690
}
1687
1691
case _ => // (3)
1688
1692
tp2 match
1689
1693
case tp2 : MethodType => true // (3a)
1690
1694
case tp2 : PolyType if tp2.resultType.isInstanceOf [MethodType ] => true // (3a)
1691
1695
case tp2 : PolyType => // (3b)
1692
- explore(isAsSpecificValueType (tp1, instantiateWithTypeVars(tp2)))
1696
+ explore(isAsGoodValueType (tp1, instantiateWithTypeVars(tp2)))
1693
1697
case _ => // 3b)
1694
- isAsSpecificValueType (tp1, tp2)
1698
+ isAsGoodValueType (tp1, tp2)
1695
1699
}
1696
1700
1697
- /** Test whether value type `tp1` is as specific as value type `tp2`.
1698
- * Let's abbreviate this to `tp1 <:s tp2`.
1699
- * Previously, `<:s` was the same as `<:`. This behavior is still
1700
- * available under mode `Mode.OldOverloadingResolution`. The new behavior
1701
- * is different, however. Here, `T <:s U` iff
1701
+ /** Test whether value type `tp1` is as good as value type `tp2`.
1702
+ * Let's abbreviate this to `tp1 <:p tp2`. The behavior depends on the Scala version
1703
+ * and mode.
1702
1704
*
1703
- * flip(T) <: flip(U)
1705
+ * - In Scala 2, `<:p` was the same as `<:`. This behavior is still
1706
+ * available in 3.0-migration if mode `Mode.OldOverloadingResolution` is turned on as well.
1707
+ * It is used to highlight differences between Scala 2 and 3 behavior.
1704
1708
*
1705
- * where `flip` changes covariant occurrences of contravariant type parameters to
1706
- * covariant ones. Intuitively `<:s` means subtyping `<:`, except that all arguments
1707
- * to contravariant parameters are compared as if they were covariant. E.g. given class
1709
+ * - In Scala 3.0-3.4, the behavior is as follows: `T <:p U` iff there is an impliit conversion
1710
+ * from `T` to `U`, or
1708
1711
*
1709
- * class Cmp[-X]
1712
+ * flip(T) <: flip(U)
1710
1713
*
1711
- * `Cmp[T] <:s Cmp[U]` if `T <: U`. On the other hand, non-variant occurrences
1712
- * of parameters are not affected. So `T <: U` would imply `Set[Cmp[U]] <:s Set[Cmp[T]]`,
1713
- * as usual, because `Set` is non-variant.
1714
+ * where `flip` changes covariant occurrences of contravariant type parameters to
1715
+ * covariant ones. Intuitively `<:p` means subtyping `<:`, except that all arguments
1716
+ * to contravariant parameters are compared as if they were covariant. E.g. given class
1714
1717
*
1715
- * This relation might seem strange, but it models closely what happens for methods.
1716
- * Indeed, if we integrate the existing rules for methods into `<:s` we have now that
1718
+ * class Cmp[-X]
1717
1719
*
1718
- * (T)R <:s (U)R
1720
+ * `Cmp[T] <:p Cmp[U]` if `T <: U`. On the other hand, non-variant occurrences
1721
+ * of parameters are not affected. So `T <: U` would imply `Set[Cmp[U]] <:p Set[Cmp[T]]`,
1722
+ * as usual, because `Set` is non-variant.
1719
1723
*
1720
- * iff
1724
+ * - From Scala 3.5, `T <:p U` means `T <: U` or `T` convertible to `U`
1725
+ * for overloading resolution (when `preferGeneral is false), and the opposite relation
1726
+ * `U <: T` or `U convertible to `T` for implicit disambiguation between givens
1727
+ * (when `preferGeneral` is true). For old-style implicit values, the 3.4 behavior is kept.
1721
1728
*
1722
- * T => R <:s U => R
1729
+ * - In Scala 3.5-migration, use the 3.5 scheme normally, and the 3.4 scheme if
1730
+ * `Mode.OldOverloadingResolution` is on. This is used to highlight differences in the
1731
+ * two resolution schemes.
1723
1732
*
1724
- * Also: If a compared type refers to a given or its module class, use
1733
+ * Also and only for given resolution : If a compared type refers to a given or its module class, use
1725
1734
* the intersection of its parent classes instead.
1726
1735
*/
1727
- def isAsSpecificValueType (tp1 : Type , tp2 : Type )(using Context ) =
1728
- if ! preferGeneral || ctx.mode.is(Mode .OldOverloadingResolution ) then
1729
- // Normal specificity test for overloading resultion (where `preferGeneral` is false)
1736
+ def isAsGoodValueType (tp1 : Type , tp2 : Type )(using Context ) =
1737
+ val oldResolution = ctx.mode.is(Mode .OldOverloadingResolution )
1738
+ if ! preferGeneral || Feature .migrateTo3 && oldResolution then
1739
+ // Normal specificity test for overloading resolution (where `preferGeneral` is false)
1730
1740
// and in mode Scala3-migration when we compare with the old Scala 2 rules.
1731
1741
isCompatible(tp1, tp2)
1732
1742
else
1733
- val flip = new TypeMap {
1734
- def apply (t : Type ) = t match {
1735
- case t @ AppliedType (tycon, args) =>
1736
- def mapArg (arg : Type , tparam : TypeParamInfo ) =
1737
- if (variance > 0 && tparam.paramVarianceSign < 0 ) defn.FunctionNOf (arg :: Nil , defn.UnitType )
1738
- else arg
1739
- mapOver(t.derivedAppliedType(tycon, args.zipWithConserve(tycon.typeParams)(mapArg)))
1740
- case _ => mapOver(t)
1741
- }
1742
- }
1743
-
1744
1743
def prepare (tp : Type ) = tp.stripTypeVar match
1745
1744
case tp : NamedType if tp.symbol.is(Module ) && tp.symbol.sourceModule.is(Given ) =>
1746
1745
tp.widen.widenToParents
@@ -1749,11 +1748,26 @@ trait Applications extends Compatibility {
1749
1748
1750
1749
val tp1p = prepare(tp1)
1751
1750
val tp2p = prepare(tp2)
1752
- if newGivenRules then
1753
- (tp2p relaxed_<:< tp1p) || viewExists(tp2, tp1)
1754
- else
1751
+
1752
+ if Feature .sourceVersion.isAtMost(SourceVersion .`3.3`) // !!! change to 3.4
1753
+ || oldResolution
1754
+ || ! compareGivens
1755
+ then
1756
+ // Intermediate rules: better means specialize, but map all type arguments downwards
1757
+ // These are enabled for 3.0-3.4, and in 3.5-migration when we compare with previous rules
1758
+ val flip = new TypeMap :
1759
+ def apply (t : Type ) = t match
1760
+ case t @ AppliedType (tycon, args) =>
1761
+ def mapArg (arg : Type , tparam : TypeParamInfo ) =
1762
+ if (variance > 0 && tparam.paramVarianceSign < 0 ) defn.FunctionNOf (arg :: Nil , defn.UnitType )
1763
+ else arg
1764
+ mapOver(t.derivedAppliedType(tycon, args.zipWithConserve(tycon.typeParams)(mapArg)))
1765
+ case _ => mapOver(t)
1755
1766
(flip(tp1p) relaxed_<:< flip(tp2p)) || viewExists(tp1, tp2)
1756
- end isAsSpecificValueType
1767
+ else
1768
+ // New rules: better means generalize
1769
+ (tp2p relaxed_<:< tp1p) || viewExists(tp2, tp1)
1770
+ end isAsGoodValueType
1757
1771
1758
1772
/** Widen the result type of synthetic given methods from the implementation class to the
1759
1773
* type that's implemented. Example
@@ -1786,8 +1800,8 @@ trait Applications extends Compatibility {
1786
1800
1787
1801
def compareWithTypes (tp1 : Type , tp2 : Type ) = {
1788
1802
val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner)
1789
- val winsType1 = isAsSpecific (alt1, tp1, alt2, tp2)
1790
- def winsType2 = isAsSpecific (alt2, tp2, alt1, tp1)
1803
+ val winsType1 = isAsGood (alt1, tp1, alt2, tp2)
1804
+ def winsType2 = isAsGood (alt2, tp2, alt1, tp1)
1791
1805
1792
1806
overload.println(i " compare( $alt1, $alt2)? $tp1 $tp2 $ownerScore $winsType1 $winsType2" )
1793
1807
if winsType1 && winsType2
0 commit comments