Skip to content

Commit 5b73fff

Browse files
committed
Refine implicit priority change warnings
Fixes #21036 Fixes #20572
1 parent cd8c5ed commit 5b73fff

File tree

8 files changed

+47
-22
lines changed

8 files changed

+47
-22
lines changed

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

+22-10
Original file line numberDiff line numberDiff line change
@@ -1302,9 +1302,8 @@ trait Implicits:
13021302

13031303
// A map that associates a priority change warning (between -source 3.4 and 3.6)
13041304
// 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)]()
1305+
// message if one of the critical candidates is part of the result of the implicit search.
1306+
val priorityChangeWarnings = mutable.ListBuffer[(/*critical:*/ List[TermRef], Message)]()
13081307

13091308
/** Compare `alt1` with `alt2` to determine which one should be chosen.
13101309
*
@@ -1319,11 +1318,16 @@ trait Implicits:
13191318
* return new result with preferGeneral = true
13201319
* 3.6 and higher: compare with preferGeneral = true
13211320
*
1321+
* @param only2ndCritical If true only the second alternative is critical in case
1322+
* of a priority change.
13221323
*/
1323-
def compareAlternatives(alt1: RefAndLevel, alt2: RefAndLevel): Int =
1324+
def compareAlternatives(alt1: RefAndLevel, alt2: RefAndLevel, only2ndCritical: Boolean = false): Int =
13241325
def comp(using Context) = explore(compare(alt1.ref, alt2.ref, preferGeneral = true))
13251326
def warn(msg: Message) =
1326-
priorityChangeWarnings += ((alt1.ref, alt2.ref, msg))
1327+
val critical =
1328+
if only2ndCritical then alt2.ref :: Nil
1329+
else alt1.ref :: alt2.ref :: Nil
1330+
priorityChangeWarnings += ((critical, msg))
13271331
if alt1.ref eq alt2.ref then 0
13281332
else if alt1.level != alt2.level then alt1.level - alt2.level
13291333
else
@@ -1443,8 +1447,8 @@ trait Implicits:
14431447
compareAlternatives(newCand, cand) > 0)
14441448
else
14451449
// keep only warnings that don't involve the failed candidate reference
1446-
priorityChangeWarnings.filterInPlace: (ref1, ref2, _) =>
1447-
ref1 != cand.ref && ref2 != cand.ref
1450+
priorityChangeWarnings.filterInPlace: (critical, _) =>
1451+
!critical.contains(cand.ref)
14481452
rank(remaining, found, fail :: rfailures)
14491453
case best: SearchSuccess =>
14501454
if (ctx.mode.is(Mode.ImplicitExploration) || isCoherent)
@@ -1454,7 +1458,15 @@ trait Implicits:
14541458
val newPending =
14551459
if (retained eq found) || remaining.isEmpty then remaining
14561460
else remaining.filterConserve(cand =>
1457-
compareAlternatives(retained, cand) <= 0)
1461+
compareAlternatives(retained, cand, only2ndCritical = true) <= 0)
1462+
// Here we drop some pending alternatives but retain in each case
1463+
// `retained`. Therefore, it's a priorty change only if the
1464+
// second alternative appears in the final search result. Otherwise
1465+
// we have the following scenario:
1466+
// - 1st alternative, bit not snd appears in final result
1467+
// - Hence, snd was eliminated either here, or otherwise by a direct
1468+
// comparison later.
1469+
// - Hence, no change in resolution.
14581470
rank(newPending, retained, rfailures)
14591471
case fail: SearchFailure =>
14601472
// The ambiguity happened in the current search: to recover we
@@ -1601,8 +1613,8 @@ trait Implicits:
16011613
throw ex
16021614

16031615
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
1616+
for (critical, msg) <- priorityChangeWarnings do
1617+
if result.found.exists(critical.contains(_)) then
16061618
report.warning(msg, srcPos)
16071619
result
16081620
end searchImplicit
File renamed without changes.
File renamed without changes.

Diff for: tests/pos/i20572.scala

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//> using options -Werror
2+
trait Writes[T]
3+
trait Format[T] extends Writes[T]
4+
given [T: List]: Writes[T] = null
5+
given [T]: Format[T] = null
6+
7+
val _ = summon[Writes[Int]]

Diff for: tests/pos/i21036.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//> using options -source 3.5 -Werror
2+
trait SameRuntime[A, B]
3+
trait BSONWriter[T]
4+
trait BSONHandler[T] extends BSONWriter[T]
5+
6+
opaque type Id = String
7+
object Id:
8+
given SameRuntime[Id, String] = ???
9+
10+
given BSONHandler[String] = ???
11+
given [T: BSONHandler]: BSONHandler[List[T]] = ???
12+
13+
given opaqueWriter[T, A](using rs: SameRuntime[T, A], writer: BSONWriter[A]): BSONWriter[T] = ???
14+
15+
val x = summon[BSONHandler[List[Id]]] // this doesn't emit warning
16+
val y = summon[BSONWriter[List[Id]]] // this did emit warning

Diff for: tests/run/given-triangle.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import language.future
1+
import language.`3.6`
22

33
class A
44
class B extends A

Diff for: tests/warn/bson.check

-10
This file was deleted.

Diff for: tests/warn/given-triangle.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//> using options -source 3.6-migration
1+
//> using options -source 3.5
22

33
class A
44
class B extends A

0 commit comments

Comments
 (0)