Skip to content

Commit 6e86ada

Browse files
authored
Priority warning fix alternative (#20487)
Warn about priority change in implicit search only if one of the participating candidates appears in the final result. It could be that we have an priority change between two ranked candidates that both are superseded by the result of the implicit search. In this case, no warning needs to be reported. This PR is #20480 with different code for the last commit. I tried to avoid entangling the priority handling too much in the returns types and spent a side effect instead. I believe it's more efficient that way, since priority warnings are very rare. Fixes #20484
2 parents d1b51dc + 3112bb7 commit 6e86ada

File tree

5 files changed

+46
-13
lines changed

5 files changed

+46
-13
lines changed

Diff for: compiler/src/dotty/tools/dotc/typer/Implicits.scala

+33-7
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,12 @@ object Implicits:
419419
sealed abstract class SearchResult extends Showable {
420420
def tree: Tree
421421
def toText(printer: Printer): Text = printer.toText(this)
422+
423+
/** The references that were found, there can be two of them in the case
424+
* of an AmbiguousImplicits failure
425+
*/
426+
def found: List[TermRef]
427+
422428
def recoverWith(other: SearchFailure => SearchResult): SearchResult = this match {
423429
case _: SearchSuccess => this
424430
case fail: SearchFailure => other(fail)
@@ -434,13 +440,17 @@ object Implicits:
434440
* @param tstate The typer state to be committed if this alternative is chosen
435441
*/
436442
case class SearchSuccess(tree: Tree, ref: TermRef, level: Int, isExtension: Boolean = false)(val tstate: TyperState, val gstate: GadtConstraint)
437-
extends SearchResult with RefAndLevel with Showable
443+
extends SearchResult with RefAndLevel with Showable:
444+
final def found = ref :: Nil
438445

439446
/** A failed search */
440447
case class SearchFailure(tree: Tree) extends SearchResult {
441448
require(tree.tpe.isInstanceOf[SearchFailureType], s"unexpected type for ${tree}")
442449
final def isAmbiguous: Boolean = tree.tpe.isInstanceOf[AmbiguousImplicits | TooUnspecific]
443450
final def reason: SearchFailureType = tree.tpe.asInstanceOf[SearchFailureType]
451+
final def found = tree.tpe match
452+
case tpe: AmbiguousImplicits => tpe.alt1.ref :: tpe.alt2.ref :: Nil
453+
case _ => Nil
444454
}
445455

446456
object SearchFailure {
@@ -1290,6 +1300,12 @@ trait Implicits:
12901300
/** Search a list of eligible implicit references */
12911301
private def searchImplicit(eligible: List[Candidate], contextual: Boolean): SearchResult =
12921302

1303+
// A map that associates a priority change warning (between -source 3.4 and 3.6)
1304+
// with the candidate refs mentioned in the warning. We report the associated
1305+
// message if both candidates qualify in tryImplicit and at least one of the candidates
1306+
// is part of the result of the implicit search.
1307+
val priorityChangeWarnings = mutable.ListBuffer[(TermRef, TermRef, Message)]()
1308+
12931309
/** Compare `alt1` with `alt2` to determine which one should be chosen.
12941310
*
12951311
* @return a number > 0 if `alt1` is preferred over `alt2`
@@ -1306,6 +1322,8 @@ trait Implicits:
13061322
*/
13071323
def compareAlternatives(alt1: RefAndLevel, alt2: RefAndLevel): Int =
13081324
def comp(using Context) = explore(compare(alt1.ref, alt2.ref, preferGeneral = true))
1325+
def warn(msg: Message) =
1326+
priorityChangeWarnings += ((alt1.ref, alt2.ref, msg))
13091327
if alt1.ref eq alt2.ref then 0
13101328
else if alt1.level != alt2.level then alt1.level - alt2.level
13111329
else
@@ -1319,16 +1337,16 @@ trait Implicits:
13191337
case 1 => "the first alternative"
13201338
case _ => "none - it's ambiguous"
13211339
if sv.stable == SourceVersion.`3.5` then
1322-
report.warning(
1340+
warn(
13231341
em"""Given search preference for $pt between alternatives ${alt1.ref} and ${alt2.ref} will change
13241342
|Current choice : ${choice(prev)}
1325-
|New choice from Scala 3.6: ${choice(cmp)}""", srcPos)
1343+
|New choice from Scala 3.6: ${choice(cmp)}""")
13261344
prev
13271345
else
1328-
report.warning(
1346+
warn(
13291347
em"""Change in given search preference for $pt between alternatives ${alt1.ref} and ${alt2.ref}
13301348
|Previous choice : ${choice(prev)}
1331-
|New choice from Scala 3.6: ${choice(cmp)}""", srcPos)
1349+
|New choice from Scala 3.6: ${choice(cmp)}""")
13321350
cmp
13331351
else cmp
13341352
else cmp
@@ -1423,7 +1441,11 @@ trait Implicits:
14231441
// need a candidate better than `cand`
14241442
healAmbiguous(fail, newCand =>
14251443
compareAlternatives(newCand, cand) > 0)
1426-
else rank(remaining, found, fail :: rfailures)
1444+
else
1445+
// keep only warnings that don't involve the failed candidate reference
1446+
priorityChangeWarnings.filterInPlace: (ref1, ref2, _) =>
1447+
ref1 != cand.ref && ref2 != cand.ref
1448+
rank(remaining, found, fail :: rfailures)
14271449
case best: SearchSuccess =>
14281450
if (ctx.mode.is(Mode.ImplicitExploration) || isCoherent)
14291451
best
@@ -1578,7 +1600,11 @@ trait Implicits:
15781600
validateOrdering(ord)
15791601
throw ex
15801602

1581-
rank(sort(eligible), NoMatchingImplicitsFailure, Nil)
1603+
val result = rank(sort(eligible), NoMatchingImplicitsFailure, Nil)
1604+
for (ref1, ref2, msg) <- priorityChangeWarnings do
1605+
if result.found.exists(ref => ref == ref1 || ref == ref2) then
1606+
report.warning(msg, srcPos)
1607+
result
15821608
end searchImplicit
15831609

15841610
def isUnderSpecifiedArgument(tp: Type): Boolean =

Diff for: tests/pos/i20484.scala

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
given Int = ???
2+
given Char = ???
3+
val a = summon[Int]

Diff for: tests/semanticdb/expect/InventedNames.expect.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ given [T/*<-givens::InventedNames$package.given_Z_T#[T]*/]: Z/*->givens::Z#*/[T/
3232

3333
val a/*<-givens::InventedNames$package.a.*/ = intValue/*->givens::InventedNames$package.intValue.*/
3434
val b/*<-givens::InventedNames$package.b.*/ = given_String/*->givens::InventedNames$package.given_String.*/
35-
//val c = given_Double
35+
val c/*<-givens::InventedNames$package.c.*/ = given_Double/*->givens::InventedNames$package.given_Double().*/
3636
val d/*<-givens::InventedNames$package.d.*/ = given_List_T/*->givens::InventedNames$package.given_List_T().*/[Int/*->scala::Int#*/]
3737
val e/*<-givens::InventedNames$package.e.*/ = given_Char/*->givens::InventedNames$package.given_Char.*/
3838
val f/*<-givens::InventedNames$package.f.*/ = given_Float/*->givens::InventedNames$package.given_Float.*/

Diff for: tests/semanticdb/expect/InventedNames.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ given [T]: Z[T] with
3232

3333
val a = intValue
3434
val b = given_String
35-
//val c = given_Double
35+
val c = given_Double
3636
val d = given_List_T[Int]
3737
val e = given_Char
3838
val f = given_Float

Diff for: tests/semanticdb/metac.expect

+8-4
Original file line numberDiff line numberDiff line change
@@ -2093,15 +2093,16 @@ Schema => SemanticDB v4
20932093
Uri => InventedNames.scala
20942094
Text => empty
20952095
Language => Scala
2096-
Symbols => 44 entries
2097-
Occurrences => 64 entries
2098-
Synthetics => 2 entries
2096+
Symbols => 45 entries
2097+
Occurrences => 66 entries
2098+
Synthetics => 3 entries
20992099

21002100
Symbols:
2101-
givens/InventedNames$package. => final package object givens extends Object { self: givens.type => +23 decls }
2101+
givens/InventedNames$package. => final package object givens extends Object { self: givens.type => +24 decls }
21022102
givens/InventedNames$package.`* *`. => final implicit lazy val given method * * Long
21032103
givens/InventedNames$package.a. => val method a Int
21042104
givens/InventedNames$package.b. => val method b String
2105+
givens/InventedNames$package.c. => val method c Double
21052106
givens/InventedNames$package.d. => val method d List[Int]
21062107
givens/InventedNames$package.e. => val method e Char
21072108
givens/InventedNames$package.f. => val method f Float
@@ -2192,6 +2193,8 @@ Occurrences:
21922193
[32:8..32:16): intValue -> givens/InventedNames$package.intValue.
21932194
[33:4..33:5): b <- givens/InventedNames$package.b.
21942195
[33:8..33:20): given_String -> givens/InventedNames$package.given_String.
2196+
[34:4..34:5): c <- givens/InventedNames$package.c.
2197+
[34:8..34:20): given_Double -> givens/InventedNames$package.given_Double().
21952198
[35:4..35:5): d <- givens/InventedNames$package.d.
21962199
[35:8..35:20): given_List_T -> givens/InventedNames$package.given_List_T().
21972200
[35:21..35:24): Int -> scala/Int#
@@ -2211,6 +2214,7 @@ Occurrences:
22112214

22122215
Synthetics:
22132216
[24:0..24:0): => *(x$1)
2217+
[34:8..34:20):given_Double => *(intValue)
22142218
[40:8..40:15):given_Y => *(given_X)
22152219

22162220
expect/Issue1749.scala

0 commit comments

Comments
 (0)