@@ -50,8 +50,14 @@ object Implicits:
5050 def implicitName (using Context ): TermName = alias
5151 }
5252
53+ /** Both search candidates and successes are references with a specific nesting level. */
54+ sealed trait RefAndLevel {
55+ def ref : TermRef
56+ def level : Int
57+ }
58+
5359 /** An eligible implicit candidate, consisting of an implicit reference and a nesting level */
54- case class Candidate (implicitRef : ImplicitRef , kind : Candidate .Kind , level : Int ) {
60+ case class Candidate (implicitRef : ImplicitRef , kind : Candidate .Kind , level : Int ) extends RefAndLevel {
5561 def ref : TermRef = implicitRef.underlyingRef
5662
5763 def isExtension = (kind & Candidate .Extension ) != 0
@@ -385,7 +391,8 @@ object Implicits:
385391 * @param level The level where the reference was found
386392 * @param tstate The typer state to be committed if this alternative is chosen
387393 */
388- case class SearchSuccess (tree : Tree , ref : TermRef , level : Int )(val tstate : TyperState , val gstate : GadtConstraint ) extends SearchResult with Showable
394+ case class SearchSuccess (tree : Tree , ref : TermRef , level : Int )(val tstate : TyperState , val gstate : GadtConstraint )
395+ extends SearchResult with RefAndLevel with Showable
389396
390397 /** A failed search */
391398 case class SearchFailure (tree : Tree ) extends SearchResult {
@@ -1124,21 +1131,24 @@ trait Implicits:
11241131 /** Search a list of eligible implicit references */
11251132 private def searchImplicit (eligible : List [Candidate ], contextual : Boolean ): SearchResult =
11261133
1127- /** Compare previous success with reference and level to determine which one would be chosen, if
1128- * an implicit starting with the reference was found.
1134+ /** Compare `alt1` with `alt2` to determine which one should be chosen.
1135+ *
1136+ * @return a number > 0 if `alt1` is preferred over `alt2`
1137+ * a number < 0 if `alt2` is preferred over `alt1`
1138+ * 0 if neither alternative is preferred over the other
11291139 */
1130- def compareCandidate ( prev : SearchSuccess , ref : TermRef , level : Int ): Int =
1131- if (prev .ref eq ref) 0
1132- else if (prev .level != level) prev .level - level
1133- else explore(compare(prev .ref, ref))(using nestedContext())
1140+ def compareAlternatives ( alt1 : RefAndLevel , alt2 : RefAndLevel ): Int =
1141+ if alt1 .ref eq alt2. ref then 0
1142+ else if alt1 .level != alt2. level then alt1 .level - alt2. level
1143+ else explore(compare(alt1 .ref, alt2. ref))(using nestedContext())
11341144
11351145 /** If `alt1` is also a search success, try to disambiguate as follows:
11361146 * - If alt2 is preferred over alt1, pick alt2, otherwise return an
11371147 * ambiguous implicits error.
11381148 */
11391149 def disambiguate (alt1 : SearchResult , alt2 : SearchSuccess ) = alt1 match
11401150 case alt1 : SearchSuccess =>
1141- var diff = compareCandidate (alt1, alt2.ref, alt2.level )
1151+ var diff = compareAlternatives (alt1, alt2)
11421152 assert(diff <= 0 ) // diff > 0 candidates should already have been eliminated in `rank`
11431153 if diff == 0 then
11441154 // Fall back: if both results are extension method applications,
@@ -1159,18 +1169,6 @@ trait Implicits:
11591169 else SearchFailure (new AmbiguousImplicits (alt1, alt2, pt, argument))
11601170 case _ : SearchFailure => alt2
11611171
1162- /** Faced with an ambiguous implicits failure `fail`, try to find another
1163- * alternative among `pending` that is strictly better than both ambiguous
1164- * alternatives. If that fails, return `fail`
1165- */
1166- def healAmbiguous (pending : List [Candidate ], fail : SearchFailure ) = {
1167- val ambi = fail.reason.asInstanceOf [AmbiguousImplicits ]
1168- val newPending = pending.filter(cand =>
1169- compareCandidate(ambi.alt1, cand.ref, cand.level) < 0 &&
1170- compareCandidate(ambi.alt2, cand.ref, cand.level) < 0 )
1171- rank(newPending, fail, Nil ).recoverWith(_ => fail)
1172- }
1173-
11741172 /** Try to find a best matching implicit term among all the candidates in `pending`.
11751173 * @param pending The list of candidates that remain to be tested
11761174 * @param found The result obtained from previously tried candidates
@@ -1184,14 +1182,22 @@ trait Implicits:
11841182 * worse than the successful candidate.
11851183 * If a trial failed:
11861184 * - if the query term is a `Not[T]` treat it as a success,
1187- * - otherwise, if the failure is an ambiguity, try to heal it (see @ healAmbiguous)
1185+ * - otherwise, if the failure is an ambiguity, try to heal it (see ` healAmbiguous` )
11881186 * and return an ambiguous error otherwise. However, under Scala2 mode this is
11891187 * treated as a simple failure, with a warning that semantics will change.
11901188 * - otherwise add the failure to `rfailures` and continue testing the other candidates.
11911189 */
11921190 def rank (pending : List [Candidate ], found : SearchResult , rfailures : List [SearchFailure ]): SearchResult =
11931191 pending match {
11941192 case cand :: remaining =>
1193+ /** To recover from an ambiguous implicit failure, we need to find a pending
1194+ * candidate that is strictly better than the failed candidate(s).
1195+ * If no such candidate is found, we propagate the ambiguity.
1196+ */
1197+ def healAmbiguous (fail : SearchFailure , betterThanFailed : Candidate => Boolean ) =
1198+ val newPending = remaining.filter(betterThanFailed)
1199+ rank(newPending, fail, Nil ).recoverWith(_ => fail)
1200+
11951201 negateIfNot(tryImplicit(cand, contextual)) match {
11961202 case fail : SearchFailure =>
11971203 if (fail.isAmbiguous)
@@ -1200,7 +1206,11 @@ trait Implicits:
12001206 if (result.isSuccess)
12011207 warnAmbiguousNegation(fail.reason.asInstanceOf [AmbiguousImplicits ])
12021208 result
1203- else healAmbiguous(remaining, fail)
1209+ else
1210+ // The ambiguity happened in a nested search: to recover we
1211+ // need a candidate better than `cand`
1212+ healAmbiguous(fail, newCand =>
1213+ compareAlternatives(newCand, cand) > 0 )
12041214 else rank(remaining, found, fail :: rfailures)
12051215 case best : SearchSuccess =>
12061216 if (ctx.mode.is(Mode .ImplicitExploration ) || isCoherent)
@@ -1210,10 +1220,15 @@ trait Implicits:
12101220 val newPending =
12111221 if (retained eq found) || remaining.isEmpty then remaining
12121222 else remaining.filterConserve(cand =>
1213- compareCandidate (retained, cand.ref, cand.level ) <= 0 )
1223+ compareAlternatives (retained, cand) <= 0 )
12141224 rank(newPending, retained, rfailures)
12151225 case fail : SearchFailure =>
1216- healAmbiguous(remaining, fail)
1226+ // The ambiguity happened in the current search: to recover we
1227+ // need a candidate better than the two ambiguous alternatives.
1228+ val ambi = fail.reason.asInstanceOf [AmbiguousImplicits ]
1229+ healAmbiguous(fail, newCand =>
1230+ compareAlternatives(newCand, ambi.alt1) > 0 &&
1231+ compareAlternatives(newCand, ambi.alt2) > 0 )
12171232 }
12181233 }
12191234 case nil =>
0 commit comments