@@ -50,8 +50,14 @@ object Implicits:
50
50
def implicitName (using Context ): TermName = alias
51
51
}
52
52
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
+
53
59
/** 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 {
55
61
def ref : TermRef = implicitRef.underlyingRef
56
62
57
63
def isExtension = (kind & Candidate .Extension ) != 0
@@ -385,7 +391,8 @@ object Implicits:
385
391
* @param level The level where the reference was found
386
392
* @param tstate The typer state to be committed if this alternative is chosen
387
393
*/
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
389
396
390
397
/** A failed search */
391
398
case class SearchFailure (tree : Tree ) extends SearchResult {
@@ -1124,21 +1131,24 @@ trait Implicits:
1124
1131
/** Search a list of eligible implicit references */
1125
1132
private def searchImplicit (eligible : List [Candidate ], contextual : Boolean ): SearchResult =
1126
1133
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
1129
1139
*/
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())
1134
1144
1135
1145
/** If `alt1` is also a search success, try to disambiguate as follows:
1136
1146
* - If alt2 is preferred over alt1, pick alt2, otherwise return an
1137
1147
* ambiguous implicits error.
1138
1148
*/
1139
1149
def disambiguate (alt1 : SearchResult , alt2 : SearchSuccess ) = alt1 match
1140
1150
case alt1 : SearchSuccess =>
1141
- var diff = compareCandidate (alt1, alt2.ref, alt2.level )
1151
+ var diff = compareAlternatives (alt1, alt2)
1142
1152
assert(diff <= 0 ) // diff > 0 candidates should already have been eliminated in `rank`
1143
1153
if diff == 0 then
1144
1154
// Fall back: if both results are extension method applications,
@@ -1159,18 +1169,6 @@ trait Implicits:
1159
1169
else SearchFailure (new AmbiguousImplicits (alt1, alt2, pt, argument))
1160
1170
case _ : SearchFailure => alt2
1161
1171
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
-
1174
1172
/** Try to find a best matching implicit term among all the candidates in `pending`.
1175
1173
* @param pending The list of candidates that remain to be tested
1176
1174
* @param found The result obtained from previously tried candidates
@@ -1184,14 +1182,22 @@ trait Implicits:
1184
1182
* worse than the successful candidate.
1185
1183
* If a trial failed:
1186
1184
* - 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` )
1188
1186
* and return an ambiguous error otherwise. However, under Scala2 mode this is
1189
1187
* treated as a simple failure, with a warning that semantics will change.
1190
1188
* - otherwise add the failure to `rfailures` and continue testing the other candidates.
1191
1189
*/
1192
1190
def rank (pending : List [Candidate ], found : SearchResult , rfailures : List [SearchFailure ]): SearchResult =
1193
1191
pending match {
1194
1192
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
+
1195
1201
negateIfNot(tryImplicit(cand, contextual)) match {
1196
1202
case fail : SearchFailure =>
1197
1203
if (fail.isAmbiguous)
@@ -1200,7 +1206,11 @@ trait Implicits:
1200
1206
if (result.isSuccess)
1201
1207
warnAmbiguousNegation(fail.reason.asInstanceOf [AmbiguousImplicits ])
1202
1208
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 )
1204
1214
else rank(remaining, found, fail :: rfailures)
1205
1215
case best : SearchSuccess =>
1206
1216
if (ctx.mode.is(Mode .ImplicitExploration ) || isCoherent)
@@ -1210,10 +1220,15 @@ trait Implicits:
1210
1220
val newPending =
1211
1221
if (retained eq found) || remaining.isEmpty then remaining
1212
1222
else remaining.filterConserve(cand =>
1213
- compareCandidate (retained, cand.ref, cand.level ) <= 0 )
1223
+ compareAlternatives (retained, cand) <= 0 )
1214
1224
rank(newPending, retained, rfailures)
1215
1225
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 )
1217
1232
}
1218
1233
}
1219
1234
case nil =>
0 commit comments