Skip to content

Commit 95caecb

Browse files
Backport "Fix healAmbiguous to compareAlternatives with disambiguate = true" to 3.5.0 (#21344)
Backports #21339 to Scala 3.5.0-RC7
2 parents 1fb613f + a1882e1 commit 95caecb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+597
-105
lines changed

compiler/src/dotty/tools/dotc/config/SourceVersion.scala

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ enum SourceVersion:
1212
case `3.4-migration`, `3.4`
1313
case `3.5-migration`, `3.5`
1414
case `3.6-migration`, `3.6`
15+
case `3.7-migration`, `3.7`
1516
// !!! Keep in sync with scala.runtime.stdlibPatches.language !!!
1617
case `future-migration`, `future`
1718

compiler/src/dotty/tools/dotc/printing/Formatting.scala

+7-10
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package dotty.tools
22
package dotc
33
package printing
44

5-
import scala.language.unsafeNulls
6-
75
import scala.collection.mutable
86

97
import core.*
@@ -52,7 +50,11 @@ object Formatting {
5250
object ShowAny extends Show[Any]:
5351
def show(x: Any): Shown = x
5452

55-
class ShowImplicits3:
53+
class ShowImplicits4:
54+
given [X: Show]: Show[X | Null] with
55+
def show(x: X | Null) = if x == null then "null" else CtxShow(toStr(x.nn))
56+
57+
class ShowImplicits3 extends ShowImplicits4:
5658
given Show[Product] = ShowAny
5759

5860
class ShowImplicits2 extends ShowImplicits3:
@@ -77,15 +79,10 @@ object Formatting {
7779
given [K: Show, V: Show]: Show[Map[K, V]] with
7880
def show(x: Map[K, V]) =
7981
CtxShow(x.map((k, v) => s"${toStr(k)} => ${toStr(v)}"))
80-
end given
8182

8283
given [H: Show, T <: Tuple: Show]: Show[H *: T] with
8384
def show(x: H *: T) =
8485
CtxShow(toStr(x.head) *: toShown(x.tail).asInstanceOf[Tuple])
85-
end given
86-
87-
given [X: Show]: Show[X | Null] with
88-
def show(x: X | Null) = if x == null then "null" else CtxShow(toStr(x.nn))
8986

9087
given Show[FlagSet] with
9188
def show(x: FlagSet) = x.flagsString
@@ -148,8 +145,8 @@ object Formatting {
148145
private def treatArg(arg: Shown, suffix: String)(using Context): (String, String) = arg.runCtxShow match {
149146
case arg: Seq[?] if suffix.indexOf('%') == 0 && suffix.indexOf('%', 1) != -1 =>
150147
val end = suffix.indexOf('%', 1)
151-
val sep = StringContext.processEscapes(suffix.substring(1, end))
152-
(arg.mkString(sep), suffix.substring(end + 1))
148+
val sep = StringContext.processEscapes(suffix.substring(1, end).nn)
149+
(arg.mkString(sep), suffix.substring(end + 1).nn)
153150
case arg: Seq[?] =>
154151
(arg.map(showArg).mkString("[", ", ", "]"), suffix)
155152
case arg =>

compiler/src/dotty/tools/dotc/reporting/messages.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2955,7 +2955,7 @@ class MissingImplicitArgument(
29552955

29562956
/** Default error message for non-nested ambiguous implicits. */
29572957
def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) =
2958-
s"Ambiguous given instances: ${ambi.explanation}${location("of")}"
2958+
s"Ambiguous given instances: ${ambi.explanation}${location("of")}${ambi.priorityChangeWarningNote}"
29592959

29602960
/** Default error messages for non-ambiguous implicits, or nested ambiguous
29612961
* implicits.

compiler/src/dotty/tools/dotc/typer/Applications.scala

+47-32
Original file line numberDiff line numberDiff line change
@@ -1748,6 +1748,17 @@ trait Applications extends Compatibility {
17481748
else if sym2.is(Module) then compareOwner(sym1, cls2)
17491749
else 0
17501750

1751+
enum CompareScheme:
1752+
case Old // Normal specificity test for overloading resolution (where `preferGeneral` is false)
1753+
// and in mode Scala3-migration when we compare with the old Scala 2 rules.
1754+
1755+
case Intermediate // Intermediate rules: better means specialize, but map all type arguments downwards
1756+
// These are enabled for 3.0-3.5, or if OldImplicitResolution
1757+
// is specified, and also for all comparisons between old-style implicits,
1758+
1759+
case New // New rules: better means generalize, givens (and extensions) always beat implicits
1760+
end CompareScheme
1761+
17511762
/** Compare two alternatives of an overloaded call or an implicit search.
17521763
*
17531764
* @param alt1, alt2 Non-overloaded references indicating the two choices
@@ -1774,6 +1785,15 @@ trait Applications extends Compatibility {
17741785
*/
17751786
def compare(alt1: TermRef, alt2: TermRef, preferGeneral: Boolean = false)(using Context): Int = trace(i"compare($alt1, $alt2)", overload) {
17761787
record("resolveOverloaded.compare")
1788+
val scheme =
1789+
val oldResolution = ctx.mode.is(Mode.OldImplicitResolution)
1790+
if !preferGeneral || Feature.migrateTo3 && oldResolution then
1791+
CompareScheme.Old
1792+
else if Feature.sourceVersion.isAtMost(SourceVersion.`3.5`)
1793+
|| oldResolution
1794+
|| alt1.symbol.is(Implicit) && alt2.symbol.is(Implicit)
1795+
then CompareScheme.Intermediate
1796+
else CompareScheme.New
17771797

17781798
/** Is alternative `alt1` with type `tp1` as good as alternative
17791799
* `alt2` with type `tp2` ?
@@ -1816,17 +1836,15 @@ trait Applications extends Compatibility {
18161836
isAsGood(alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2)
18171837
}
18181838
case _ => // (3)
1819-
def isGiven(alt: TermRef) =
1820-
alt1.symbol.is(Given) && alt.symbol != defn.NotGivenClass
1821-
def compareValues(tp1: Type, tp2: Type)(using Context) =
1822-
isAsGoodValueType(tp1, tp2, isGiven(alt1), isGiven(alt2))
1839+
def compareValues(tp2: Type)(using Context) =
1840+
isAsGoodValueType(tp1, tp2, alt1.symbol.is(Implicit))
18231841
tp2 match
18241842
case tp2: MethodType => true // (3a)
18251843
case tp2: PolyType if tp2.resultType.isInstanceOf[MethodType] => true // (3a)
18261844
case tp2: PolyType => // (3b)
1827-
explore(compareValues(tp1, instantiateWithTypeVars(tp2)))
1845+
explore(compareValues(instantiateWithTypeVars(tp2)))
18281846
case _ => // 3b)
1829-
compareValues(tp1, tp2)
1847+
compareValues(tp2)
18301848
}
18311849

18321850
/** Test whether value type `tp1` is as good as value type `tp2`.
@@ -1837,7 +1855,7 @@ trait Applications extends Compatibility {
18371855
* available in 3.0-migration if mode `Mode.OldImplicitResolution` is turned on as well.
18381856
* It is used to highlight differences between Scala 2 and 3 behavior.
18391857
*
1840-
* - In Scala 3.0-3.5, the behavior is as follows: `T <:p U` iff there is an impliit conversion
1858+
* - In Scala 3.0-3.6, the behavior is as follows: `T <:p U` iff there is an implicit conversion
18411859
* from `T` to `U`, or
18421860
*
18431861
* flip(T) <: flip(U)
@@ -1852,21 +1870,20 @@ trait Applications extends Compatibility {
18521870
* of parameters are not affected. So `T <: U` would imply `Set[Cmp[U]] <:p Set[Cmp[T]]`,
18531871
* as usual, because `Set` is non-variant.
18541872
*
1855-
* - From Scala 3.6, `T <:p U` means `T <: U` or `T` convertible to `U`
1873+
* - From Scala 3.7, `T <:p U` means `T <: U` or `T` convertible to `U`
18561874
* for overloading resolution (when `preferGeneral is false), and the opposite relation
18571875
* `U <: T` or `U convertible to `T` for implicit disambiguation between givens
1858-
* (when `preferGeneral` is true). For old-style implicit values, the 3.4 behavior is kept.
1859-
* If one of the alternatives is a given and the other is an implicit, the given wins.
1876+
* (when `preferGeneral` is true). For old-style implicit values, the 3.5 behavior is kept.
1877+
* If one of the alternatives is an implicit and the other is a given (or an extension), the implicit loses.
18601878
*
1861-
* - In Scala 3.5 and Scala 3.6-migration, we issue a warning if the result under
1862-
* Scala 3.6 differ wrt to the old behavior up to 3.5.
1879+
* - In Scala 3.6 and Scala 3.7-migration, we issue a warning if the result under
1880+
* Scala 3.7 differs wrt to the old behavior up to 3.6.
18631881
*
18641882
* Also and only for given resolution: If a compared type refers to a given or its module class, use
18651883
* the intersection of its parent classes instead.
18661884
*/
1867-
def isAsGoodValueType(tp1: Type, tp2: Type, alt1isGiven: Boolean, alt2isGiven: Boolean)(using Context): Boolean =
1868-
val oldResolution = ctx.mode.is(Mode.OldImplicitResolution)
1869-
if !preferGeneral || Feature.migrateTo3 && oldResolution then
1885+
def isAsGoodValueType(tp1: Type, tp2: Type, alt1IsImplicit: Boolean)(using Context): Boolean =
1886+
if scheme == CompareScheme.Old then
18701887
// Normal specificity test for overloading resolution (where `preferGeneral` is false)
18711888
// and in mode Scala3-migration when we compare with the old Scala 2 rules.
18721889
isCompatible(tp1, tp2)
@@ -1880,13 +1897,7 @@ trait Applications extends Compatibility {
18801897
val tp1p = prepare(tp1)
18811898
val tp2p = prepare(tp2)
18821899

1883-
if Feature.sourceVersion.isAtMost(SourceVersion.`3.4`)
1884-
|| oldResolution
1885-
|| !alt1isGiven && !alt2isGiven
1886-
then
1887-
// Intermediate rules: better means specialize, but map all type arguments downwards
1888-
// These are enabled for 3.0-3.5, and for all comparisons between old-style implicits,
1889-
// and in 3.5 and 3.6-migration when we compare with previous rules.
1900+
if scheme == CompareScheme.Intermediate || alt1IsImplicit then
18901901
val flip = new TypeMap:
18911902
def apply(t: Type) = t match
18921903
case t @ AppliedType(tycon, args) =>
@@ -1897,9 +1908,7 @@ trait Applications extends Compatibility {
18971908
case _ => mapOver(t)
18981909
(flip(tp1p) relaxed_<:< flip(tp2p)) || viewExists(tp1, tp2)
18991910
else
1900-
// New rules: better means generalize, givens always beat implicits
1901-
if alt1isGiven != alt2isGiven then alt1isGiven
1902-
else (tp2p relaxed_<:< tp1p) || viewExists(tp2, tp1)
1911+
(tp2p relaxed_<:< tp1p) || viewExists(tp2, tp1)
19031912
end isAsGoodValueType
19041913

19051914
/** Widen the result type of synthetic given methods from the implementation class to the
@@ -1970,13 +1979,19 @@ trait Applications extends Compatibility {
19701979
// alternatives are the same after following ExprTypes, pick one of them
19711980
// (prefer the one that is not a method, but that's arbitrary).
19721981
if alt1.widenExpr =:= alt2 then -1 else 1
1973-
else ownerScore match
1974-
case 1 => if winsType1 || !winsType2 then 1 else 0
1975-
case -1 => if winsType2 || !winsType1 then -1 else 0
1976-
case 0 =>
1977-
if winsType1 != winsType2 then if winsType1 then 1 else -1
1978-
else if alt1.symbol == alt2.symbol then comparePrefixes
1979-
else 0
1982+
else
1983+
// For new implicit resolution, take ownerscore as more significant than type resolution
1984+
// Reason: People use owner hierarchies to explicitly prioritize, we should not
1985+
// break that by changing implicit priority of types.
1986+
def drawOrOwner =
1987+
if scheme == CompareScheme.New then ownerScore else 0
1988+
ownerScore match
1989+
case 1 => if winsType1 || !winsType2 then 1 else drawOrOwner
1990+
case -1 => if winsType2 || !winsType1 then -1 else drawOrOwner
1991+
case 0 =>
1992+
if winsType1 != winsType2 then if winsType1 then 1 else -1
1993+
else if alt1.symbol == alt2.symbol then comparePrefixes
1994+
else 0
19801995
end compareWithTypes
19811996

19821997
if alt1.symbol.is(ConstructorProxy) && !alt2.symbol.is(ConstructorProxy) then -1

0 commit comments

Comments
 (0)