Skip to content

Commit 39b4ec2

Browse files
committed
Address reviewers comments
1 parent 98c4b7d commit 39b4ec2

File tree

1 file changed

+65
-37
lines changed

1 file changed

+65
-37
lines changed

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

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ object Implicits {
305305

306306
def msg(implicit ctx: Context): Message = explanation
307307

308-
/** If search was a for an implicit conversion, a note describing the failure
308+
/** If search was for an implicit conversion, a note describing the failure
309309
* in more detail - this is either empty or starts with a '\n'
310310
*/
311311
def whyNoConversion(implicit ctx: Context): String = ""
@@ -878,14 +878,18 @@ trait Implicits { self: Typer =>
878878
def disambiguate(alt1: SearchResult, alt2: SearchSuccess) = alt1 match {
879879
case alt1: SearchSuccess =>
880880
val diff = compareCandidate(alt1, alt2.ref, alt2.level)
881-
assert(diff <= 0)
881+
assert(diff <= 0) // diff > 0 candidates should already have been eliminated in `rank`
882882
if (diff < 0) alt2
883883
else
884884
// numericValueTypeBreak(alt1, alt2) recoverWith
885885
SearchFailure(new AmbiguousImplicits(alt1, alt2, pt, argument))
886886
case _: SearchFailure => alt2
887887
}
888888

889+
/** Faced with an ambiguous implicits failure `fail`, try to find another
890+
* alternative among `pending` that is strictly better than both ambiguous
891+
* alternatives. If that fails, return `fail`
892+
*/
889893
def healAmbiguous(pending: List[Candidate], fail: SearchFailure) = {
890894
val ambi = fail.reason.asInstanceOf[AmbiguousImplicits]
891895
val newPending = pending.filter(cand =>
@@ -894,42 +898,66 @@ trait Implicits { self: Typer =>
894898
rank(newPending, fail, Nil).recoverWith(_ => fail)
895899
}
896900

897-
def rank(pending: List[Candidate], found: SearchResult, rfailures: List[SearchFailure]): SearchResult = pending match {
898-
case cand :: pending1 =>
899-
tryImplicit(cand) match {
900-
case fail: SearchFailure =>
901-
if (isNot)
902-
SearchSuccess(ref(defn.Not_value), defn.Not_value.termRef, 0)(ctx.typerState)
903-
else if (fail.isAmbiguous)
904-
if (ctx.scala2Mode) {
905-
val result = rank(pending1, found, NoMatchingImplicitsFailure :: rfailures)
906-
if (result.isSuccess)
907-
warnAmbiguousNegation(fail.reason.asInstanceOf[AmbiguousImplicits])
908-
result
909-
}
910-
else
911-
healAmbiguous(pending1, fail)
912-
else
913-
rank(pending1, found, fail :: rfailures)
914-
case best: SearchSuccess =>
915-
if (isNot)
916-
NoMatchingImplicitsFailure
917-
else if (ctx.mode.is(Mode.ImplicitExploration) || isCoherent)
918-
best
919-
else disambiguate(found, best) match {
920-
case retained: SearchSuccess =>
921-
val newPending =
922-
if (retained eq found) pending1
923-
else pending1.filter(cand =>
924-
compareCandidate(retained, cand.ref, cand.level) <= 0)
925-
rank(newPending, retained, rfailures)
926-
case fail: SearchFailure =>
927-
healAmbiguous(pending1, fail)
901+
/** Try to find a best matching implicit term among all the candidates in `pending`.
902+
* @param pending The list of candidates that remain to be tested
903+
* @param found The result obtained from previously tried candidates
904+
* @param rfailures A list of all failures from previously tried candidates in reverse order
905+
*
906+
* The scheme is to try candidates one-by-one. If a trial is successful:
907+
* - if the query term is a `Not[T]` treat it a failure,
908+
* - otherwise, if a previous search was also successful, handle the ambiguity
909+
* in `disambiguate`,
910+
* - otherwise, continue the search with all candidates that are not strictly
911+
* worse than the succesful candidate.
912+
* If a trial failed:
913+
* - if the query term is a `Not[T]` treat it as a success,
914+
* - otherwise, if the failure is an ambiguity, try to heal it (see @healAmbiguous)
915+
* and return an ambiguous error otherwise. However, under Scala2 mode this is
916+
* treated as a simple failure, with a warning that semantics will change.
917+
* - otherwise add the failure to `rfailures` and continue testing the other candidates.
918+
*/
919+
def rank(pending: List[Candidate], found: SearchResult, rfailures: List[SearchFailure]): SearchResult = {
920+
def recur(result: SearchResult, remaining: List[Candidate]): SearchResult = result match {
921+
case fail: SearchFailure =>
922+
if (isNot)
923+
recur(
924+
SearchSuccess(ref(defn.Not_value), defn.Not_value.termRef, 0)(
925+
ctx.typerState.fresh().setCommittable(true))
926+
remaining)
927+
else if (fail.isAmbiguous)
928+
if (ctx.scala2Mode) {
929+
val result = rank(remaining, found, NoMatchingImplicitsFailure :: rfailures)
930+
if (result.isSuccess)
931+
warnAmbiguousNegation(fail.reason.asInstanceOf[AmbiguousImplicits])
932+
result
928933
}
929-
}
930-
case nil =>
931-
if (rfailures.isEmpty) found
932-
else found.recoverWith(_ => rfailures.reverse.maxBy(_.tree.treeSize))
934+
else
935+
healAmbiguous(remaining, fail)
936+
else
937+
rank(remaining, found, fail :: rfailures)
938+
case best: SearchSuccess =>
939+
if (isNot)
940+
recur(NoMatchingImplicitsFailure, remaining)
941+
else if (ctx.mode.is(Mode.ImplicitExploration) || isCoherent)
942+
best
943+
else disambiguate(found, best) match {
944+
case retained: SearchSuccess =>
945+
val newPending =
946+
if (retained eq found) remaining
947+
else remaining.filter(cand =>
948+
compareCandidate(retained, cand.ref, cand.level) <= 0)
949+
rank(newPending, retained, rfailures)
950+
case fail: SearchFailure =>
951+
healAmbiguous(remaining, fail)
952+
}
953+
}
954+
pending match {
955+
case cand :: pending1 =>
956+
recur(tryImplicit(cand), pending1)
957+
case nil =>
958+
if (rfailures.isEmpty) found
959+
else found.recoverWith(_ => rfailures.reverse.maxBy(_.tree.treeSize))
960+
}
933961
}
934962

935963
def warnAmbiguousNegation(ambi: AmbiguousImplicits) =

0 commit comments

Comments
 (0)