@@ -22,7 +22,7 @@ import ProtoTypes.*
2222import Inferencing .*
2323import reporting .*
2424import Nullables .* , NullOpsDecorator .*
25- import config .Feature
25+ import config .{ Feature , SourceVersion }
2626
2727import collection .mutable
2828import config .Printers .{overload , typr , unapp }
@@ -1622,6 +1622,12 @@ trait Applications extends Compatibility {
16221622 /** Compare two alternatives of an overloaded call or an implicit search.
16231623 *
16241624 * @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.
16251631 * @return 1 if 1st alternative is preferred over 2nd
16261632 * -1 if 2nd alternative is preferred over 1st
16271633 * 0 if neither alternative is preferred over the other
@@ -1640,27 +1646,25 @@ trait Applications extends Compatibility {
16401646 def compare (alt1 : TermRef , alt2 : TermRef , preferGeneral : Boolean = false )(using Context ): Int = trace(i " compare( $alt1, $alt2) " , overload) {
16411647 record(" resolveOverloaded.compare" )
16421648
1643- val newGivenRules =
1644- ctx.mode.is(Mode .NewGivenRules ) && alt1.symbol.is(Given )
1649+ val compareGivens = alt1.symbol.is(Given ) || alt2.symbol.is(Given )
16451650
1646- /** Is alternative `alt1` with type `tp1` as specific as alternative
1651+ /** Is alternative `alt1` with type `tp1` as good as alternative
16471652 * `alt2` with type `tp2` ?
16481653 *
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`
16501655 * if `alt1` is nullary or `alt2` is applicable to arguments (p1, ..., pn) of
16511656 * types T1,...,Tn. If the last parameter `pn` has a vararg type T*, then
16521657 * `alt1` must be applicable to arbitrary numbers of `T` parameters (which
16531658 * implies that it must be a varargs method as well).
16541659 * 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
16561661 * assumption that for i = 1,...,n each ai is an abstract type name bounded
16571662 * from below by Li and from above by Ui.
16581663 * 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`
16621666 */
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) {
16641668 tp1 match
16651669 case tp1 : MethodType => // (1)
16661670 tp1.paramInfos.isEmpty && tp2.isInstanceOf [LambdaType ]
@@ -1682,65 +1686,60 @@ trait Applications extends Compatibility {
16821686 fullyDefinedType(tp1Params, " type parameters of alternative" , alt1.symbol.srcPos)
16831687
16841688 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)
16861690 }
16871691 case _ => // (3)
16881692 tp2 match
16891693 case tp2 : MethodType => true // (3a)
16901694 case tp2 : PolyType if tp2.resultType.isInstanceOf [MethodType ] => true // (3a)
16911695 case tp2 : PolyType => // (3b)
1692- explore(isAsSpecificValueType (tp1, instantiateWithTypeVars(tp2)))
1696+ explore(isAsGoodValueType (tp1, instantiateWithTypeVars(tp2)))
16931697 case _ => // 3b)
1694- isAsSpecificValueType (tp1, tp2)
1698+ isAsGoodValueType (tp1, tp2)
16951699 }
16961700
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.
17021704 *
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.
17041708 *
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
17081711 *
1709- * class Cmp[-X]
1712+ * flip(T) <: flip(U)
17101713 *
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
17141717 *
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]
17171719 *
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.
17191723 *
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.
17211728 *
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.
17231732 *
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
17251734 * the intersection of its parent classes instead.
17261735 */
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)
17301740 // and in mode Scala3-migration when we compare with the old Scala 2 rules.
17311741 isCompatible(tp1, tp2)
17321742 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-
17441743 def prepare (tp : Type ) = tp.stripTypeVar match
17451744 case tp : NamedType if tp.symbol.is(Module ) && tp.symbol.sourceModule.is(Given ) =>
17461745 tp.widen.widenToParents
@@ -1749,11 +1748,26 @@ trait Applications extends Compatibility {
17491748
17501749 val tp1p = prepare(tp1)
17511750 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)
17551766 (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
17571771
17581772 /** Widen the result type of synthetic given methods from the implementation class to the
17591773 * type that's implemented. Example
@@ -1786,8 +1800,8 @@ trait Applications extends Compatibility {
17861800
17871801 def compareWithTypes (tp1 : Type , tp2 : Type ) = {
17881802 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)
17911805
17921806 overload.println(i " compare( $alt1, $alt2)? $tp1 $tp2 $ownerScore $winsType1 $winsType2" )
17931807 if winsType1 && winsType2
0 commit comments