Skip to content

Commit 1474e69

Browse files
committed
Fix priority change logic for ranking
As worked out in collaboration with @EugeneFlesselle
1 parent 4bc7c10 commit 1474e69

File tree

5 files changed

+41
-22
lines changed

5 files changed

+41
-22
lines changed

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

+15-22
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,9 @@ trait Implicits:
13051305
// message if one of the critical candidates is part of the result of the implicit search.
13061306
val priorityChangeWarnings = mutable.ListBuffer[(/*critical:*/ List[TermRef], Message)]()
13071307

1308+
def isWarnPriorityChangeVersion(sv: SourceVersion): Boolean =
1309+
sv.stable == SourceVersion.`3.5` || sv == SourceVersion.`3.6-migration`
1310+
13081311
/** Compare `alt1` with `alt2` to determine which one should be chosen.
13091312
*
13101313
* @return a number > 0 if `alt1` is preferred over `alt2`
@@ -1321,25 +1324,21 @@ trait Implicits:
13211324
* @param disambiguate The call is used to disambiguate two successes, not for ranking.
13221325
* When ranking, we are always filtering out either > 0 or <= 0 results.
13231326
* In each case a priority change from 0 to -1 or vice versa makes no difference.
1324-
* @param only2ndCritical If true only the second alternative is critical in case
1325-
* of a priority change.
13261327
*/
1327-
def compareAlternatives(alt1: RefAndLevel, alt2: RefAndLevel, disambiguate: Boolean = false, only2ndCritical: Boolean = false): Int =
1328+
def compareAlternatives(alt1: RefAndLevel, alt2: RefAndLevel, disambiguate: Boolean = false): Int =
13281329
def comp(using Context) = explore(compare(alt1.ref, alt2.ref, preferGeneral = true))
13291330
if alt1.ref eq alt2.ref then 0
13301331
else if alt1.level != alt2.level then alt1.level - alt2.level
13311332
else
13321333
var cmp = comp(using searchContext())
13331334
val sv = Feature.sourceVersion
1334-
if sv.stable == SourceVersion.`3.5` || sv == SourceVersion.`3.6-migration` then
1335+
if isWarnPriorityChangeVersion(sv) then
13351336
val prev = comp(using searchContext().addMode(Mode.OldImplicitResolution))
1336-
if cmp != prev then
1337+
if disambiguate && cmp != prev then
13371338
def warn(msg: Message) =
1338-
if disambiguate || cmp > 0 || prev > 0 then
1339-
val critical =
1340-
if only2ndCritical then alt2.ref :: Nil
1341-
else alt1.ref :: alt2.ref :: Nil
1342-
priorityChangeWarnings += ((critical, msg))
1339+
val critical = alt1.ref :: alt2.ref :: Nil
1340+
priorityChangeWarnings += ((critical, msg))
1341+
implicits.println(i"PRIORITY CHANGE ${alt1.ref}, ${alt2.ref}, $disambiguate")
13431342
def choice(c: Int) = c match
13441343
case -1 => "the second alternative"
13451344
case 1 => "the first alternative"
@@ -1356,7 +1355,9 @@ trait Implicits:
13561355
|Previous choice : ${choice(prev)}
13571356
|New choice from Scala 3.6: ${choice(cmp)}""")
13581357
cmp
1359-
else cmp
1358+
else cmp max prev
1359+
// When ranking, we keep the better of cmp and prev, which ends up retaining a candidate
1360+
// if it is retained in either version.
13601361
else cmp
13611362
end compareAlternatives
13621363

@@ -1367,7 +1368,8 @@ trait Implicits:
13671368
def disambiguate(alt1: SearchResult, alt2: SearchSuccess) = alt1 match
13681369
case alt1: SearchSuccess =>
13691370
var diff = compareAlternatives(alt1, alt2, disambiguate = true)
1370-
assert(diff <= 0) // diff > 0 candidates should already have been eliminated in `rank`
1371+
assert(diff <= 0 || isWarnPriorityChangeVersion(Feature.sourceVersion))
1372+
// diff > 0 candidates should already have been eliminated in `rank`
13711373
if diff == 0 && alt1.ref =:= alt2.ref then
13721374
diff = 1 // See i12951 for a test where this happens
13731375
else if diff == 0 && alt2.isExtension then
@@ -1461,16 +1463,7 @@ trait Implicits:
14611463
case retained: SearchSuccess =>
14621464
val newPending =
14631465
if (retained eq found) || remaining.isEmpty then remaining
1464-
else remaining.filterConserve(cand =>
1465-
compareAlternatives(retained, cand, only2ndCritical = true) <= 0)
1466-
// Here we drop some pending alternatives but retain in each case
1467-
// `retained`. Therefore, it's a priorty change only if the
1468-
// second alternative appears in the final search result. Otherwise
1469-
// we have the following scenario:
1470-
// - 1st alternative, but not snd appears in final result
1471-
// - Hence, snd was eliminated either here, or otherwise by a direct
1472-
// comparison later.
1473-
// - Hence, no change in resolution.
1466+
else remaining.filterConserve(newCand => compareAlternatives(newCand, retained) >= 0)
14741467
rank(newPending, retained, rfailures)
14751468
case fail: SearchFailure =>
14761469
// The ambiguity happened in the current search: to recover we

tests/warn/i21036a.check

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- Warning: tests/warn/i21036a.scala:7:17 ------------------------------------------------------------------------------
2+
7 |val y = summon[A] // warn
3+
| ^
4+
| Given search preference for A between alternatives (b : B) and (a : A) will change
5+
| Current choice : the first alternative
6+
| New choice from Scala 3.6: the second alternative

tests/warn/i21036a.scala

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//> using options -source 3.5
2+
trait A
3+
trait B extends A
4+
given b: B = ???
5+
given a: A = ???
6+
7+
val y = summon[A] // warn

tests/warn/i21036b.check

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- Warning: tests/warn/i21036b.scala:7:17 ------------------------------------------------------------------------------
2+
7 |val y = summon[A] // warn
3+
| ^
4+
| Change in given search preference for A between alternatives (b : B) and (a : A)
5+
| Previous choice : the first alternative
6+
| New choice from Scala 3.6: the second alternative

tests/warn/i21036b.scala

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//> using options -source 3.6-migration
2+
trait A
3+
trait B extends A
4+
given b: B = ???
5+
given a: A = ???
6+
7+
val y = summon[A] // warn

0 commit comments

Comments
 (0)