From fe38652681bbc591f7ea6180476dcc4f3740c9ef Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 28 Mar 2014 08:40:09 +0100 Subject: [PATCH 1/7] WIP Better diagnostic for divergent implicit error --- .../tools/nsc/typechecker/ContextErrors.scala | 8 +++---- .../tools/nsc/typechecker/Implicits.scala | 22 +++++++++++++++---- test/files/neg/t5318.check | 5 ++++- test/files/neg/t5318b.check | 7 ++++-- test/files/neg/t5318c.check | 5 ++++- test/files/neg/t696.check | 10 +++++++-- 6 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 9715fdaf0042..13c4062cf4fa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -67,12 +67,12 @@ trait ContextErrors { // (pt at the point of divergence gives less information to the user) // Note: it is safe to delay error message generation in this case // becasue we don't modify implicits' infos. - case class DivergentImplicitTypeError(underlyingTree: Tree, pt0: Type, sym: Symbol) + case class DivergentImplicitTypeError(underlyingTree: Tree, pt0: Type, sym: Symbol, explanation: String) extends TreeTypeError { def errMsg: String = errMsgForPt(pt0) def withPt(pt: Type): AbsTypeError = this.copy(pt0 = pt) private def errMsgForPt(pt: Type) = - s"diverging implicit expansion for type ${pt}\nstarting with ${sym.fullLocationString}" + s"diverging implicit expansion for type ${pt}\nstarting with ${sym.fullLocationString}.\n$explanation" } case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String) @@ -1236,8 +1236,8 @@ trait ContextErrors { } } - def DivergingImplicitExpansionError(tree: Tree, pt: Type, sym: Symbol)(implicit context0: Context) = - issueTypeError(DivergentImplicitTypeError(tree, pt, sym)) + def DivergingImplicitExpansionError(tree: Tree, pt: Type, sym: Symbol, explanation: String = "")(implicit context0: Context) = + issueTypeError(DivergentImplicitTypeError(tree, pt, sym, explanation)) } object NamesDefaultsErrorsGen { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 1a38fcf3a407..6bfc4c2c4795 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -171,6 +171,7 @@ trait Implicits { def isFailure = false def isAmbiguousFailure = false def isDivergent = false + def explanation: String = "" final def isSuccess = !isFailure } @@ -178,7 +179,7 @@ trait Implicits { override def isFailure = true } - lazy val DivergentSearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter, Nil) { + final class DivergentSearchFailure(override val explanation: String) extends SearchResult(EmptyTree, EmptyTreeTypeSubstituter, Nil) { override def isFailure = true override def isDivergent = true } @@ -443,10 +444,21 @@ trait Implicits { // otherwise, the macro writer could check `c.openMacros` and `c.openImplicits` and do `c.abort` when expansions are deemed to be divergent // upon receiving `c.abort` the typechecker will decide that the corresponding implicit search has failed // which will fail the entire stack of implicit searches, producing a nice error message provided by the programmer - (context.openImplicits find { case OpenImplicit(info, tp, tree1) => !info.sym.isMacro && tree1.symbol == tree.symbol && dominates(pt, tp)}) match { + val dominatedOpenImplicit = context.openImplicits find { + case OpenImplicit(info, tp, tree1) => !info.sym.isMacro && tree1.symbol == tree.symbol && dominates(pt, tp) + } + dominatedOpenImplicit match { case Some(pending) => + val next = (OpenImplicit(info, pt, tree) :: context.openImplicits) + val msg = next.reverse.zipWithIndex.map { + case (OpenImplicit(info, pt, tree), i) => + s"${" " * i} $tree" + }.mkString("\n") //println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG - DivergentSearchFailure +// context.map { +// case OpenImplicit() +// } + new DivergentSearchFailure(msg) case None => try { context.openImplicits = OpenImplicit(info, pt, tree) :: context.openImplicits @@ -837,12 +849,14 @@ trait Implicits { // Initially null, will be saved on first diverging expansion. private var implicitSym: Symbol = _ private var countdown: Int = 1 + var explanation: String = "" def sym: Symbol = implicitSym def apply(search: SearchResult, i: ImplicitInfo): SearchResult = if (search.isDivergent && countdown > 0) { countdown -= 1 implicitSym = i.sym + explanation = search.explanation log(s"discarding divergent implicit $implicitSym during implicit search") SearchFailure } else search @@ -925,7 +939,7 @@ trait Implicits { * now we can throw it for the error message. */ if (DivergentImplicitRecovery.sym != null) { - DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym)(context) + DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym, DivergentImplicitRecovery.explanation)(context) } if (invalidImplicits.nonEmpty) diff --git a/test/files/neg/t5318.check b/test/files/neg/t5318.check index d6a3a57935d0..5e9159191b75 100644 --- a/test/files/neg/t5318.check +++ b/test/files/neg/t5318.check @@ -1,5 +1,8 @@ t5318.scala:7: error: diverging implicit expansion for type CompilerHang.this.TC[F] -starting with method tc in class CompilerHang +starting with method tc in class CompilerHang. + CompilerHang.this.breakage[F] + CompilerHang.this.tc[M] + CompilerHang.this.tc[M] breakage // type checker doesn't terminate, should report inference failure ^ one error found diff --git a/test/files/neg/t5318b.check b/test/files/neg/t5318b.check index 47a10d673382..b5c5c1a0a2be 100644 --- a/test/files/neg/t5318b.check +++ b/test/files/neg/t5318b.check @@ -1,5 +1,8 @@ t5318b.scala:7: error: diverging implicit expansion for type DivergingImplicitReported.this.TC[F] -starting with method tc in class DivergingImplicitReported +starting with method tc in class DivergingImplicitReported. + DivergingImplicitReported.this.breakage[F] + DivergingImplicitReported.this.tc[M] + DivergingImplicitReported.this.tc[M] breakage // correct: diverging implicit expansion ^ -one error found \ No newline at end of file +one error found diff --git a/test/files/neg/t5318c.check b/test/files/neg/t5318c.check index 594539be6968..bbae9349d060 100644 --- a/test/files/neg/t5318c.check +++ b/test/files/neg/t5318c.check @@ -1,5 +1,8 @@ t5318c.scala:13: error: diverging implicit expansion for type CompilerHang.this.TC[F] -starting with method tc in class CompilerHang +starting with method tc in class CompilerHang. + CompilerHang.this.breakage[F] + CompilerHang.this.tc[x, CC] + CompilerHang.this.tc[x, CC] breakage // type checker doesn't terminate, should report inference failure ^ one error found diff --git a/test/files/neg/t696.check b/test/files/neg/t696.check index b7bc5cdf98d5..93312d00ecc6 100644 --- a/test/files/neg/t696.check +++ b/test/files/neg/t696.check @@ -1,9 +1,15 @@ t696.scala:5: error: diverging implicit expansion for type TypeUtil0.Type[Any] -starting with method WithType in object TypeUtil0 +starting with method WithType in object TypeUtil0. + TypeUtil0.this.as[Any](null) + TypeUtil0.WithType[Any, Any] + TypeUtil0.WithType[Any, Any] as[Any](null) ^ t696.scala:6: error: diverging implicit expansion for type TypeUtil0.Type[X] -starting with method WithType in object TypeUtil0 +starting with method WithType in object TypeUtil0. + TypeUtil0.this.as[X](null) + TypeUtil0.WithType[S, Any] + TypeUtil0.WithType[S, T] def foo[X]() = as[X](null) ^ two errors found From a5beb54361c6b35d1d69b5c5c82f94817ba4dea9 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 30 Mar 2014 17:52:15 +0200 Subject: [PATCH 2/7] WIP SI-8460 Address regresisoni n divergent implicit recovery The test case was extracted from scalanlp/breeze. The regression occured in SI-7291 / accaa314. After that commit, the shapeless-1.2.4-scala-2.11.x branch of milessabin/shapeless also stopped; it works again after this commit. --- .../tools/nsc/typechecker/Implicits.scala | 31 +++++++++++++------ test/files/neg/divergent-implicit.check | 9 +++++- test/files/t8460.scala | 25 +++++++++++++++ 3 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 test/files/t8460.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 6bfc4c2c4795..887008a30aff 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -165,8 +165,7 @@ trait Implicits { * @param undetparams undeterminted type parameters */ class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter, val undetparams: List[Symbol]) { - override def toString = "SearchResult(%s, %s)".format(tree, - if (subst.isEmpty) "" else subst) + override def toString = "%s(%s, %s)".format(getClass.getName, tree, if (subst.isEmpty) "" else subst) def isFailure = false def isAmbiguousFailure = false @@ -175,7 +174,7 @@ trait Implicits { final def isSuccess = !isFailure } - lazy val SearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter, Nil) { + object SearchFailure extends SearchResult(EmptyTree, EmptyTreeTypeSubstituter, Nil) { override def isFailure = true } @@ -850,13 +849,18 @@ trait Implicits { private var implicitSym: Symbol = _ private var countdown: Int = 1 var explanation: String = "" + var typeErrors: List[DivergentImplicitTypeError] = Nil def sym: Symbol = implicitSym def apply(search: SearchResult, i: ImplicitInfo): SearchResult = if (search.isDivergent && countdown > 0) { countdown -= 1 + if (i.sym == implicitSym) + ??? implicitSym = i.sym explanation = search.explanation + typeErrors = context.errors.iterator.collect { case dite: DivergentImplicitTypeError => dite }.toList + context.flushBuffer() log(s"discarding divergent implicit $implicitSym during implicit search") SearchFailure } else search @@ -885,15 +889,20 @@ trait Implicits { @tailrec private def rankImplicits(pending: Infos, acc: Infos): Infos = pending match { case Nil => acc case i :: is => - DivergentImplicitRecovery(typedImplicit(i, ptChecked = true, isLocalToCallsite), i) match { + val typedImplicitResult = typedImplicit(i, ptChecked = true, isLocalToCallsite) + val depth = context.openImplicits.length + val recoveredResult = DivergentImplicitRecovery(typedImplicitResult , i) + recoveredResult match { case sr if sr.isDivergent => Nil case sr if sr.isFailure => // We don't want errors that occur during checking implicit info // to influence the check of further infos. - context.reportBuffer.retainErrors { - case err: DivergentImplicitTypeError => true + val divergents = context.reportBuffer.errors.collect { + case err: DivergentImplicitTypeError => err } + DivergentImplicitRecovery.typeErrors = divergents.toList + context.reportBuffer.clearAll() rankImplicits(is, acc) case newBest => best = newBest @@ -921,7 +930,8 @@ trait Implicits { // After calling rankImplicits, the least frequent matching one is first and // earlier elems may improve on later ones, but not the other way. // So if there is any element not improved upon by the first it is an error. - rankImplicits(eligible, Nil) match { + val ranked: Infos = rankImplicits(eligible, Nil) + ranked match { case Nil => () case chosen :: rest => rest find (alt => !improves(chosen, alt)) match { @@ -938,8 +948,11 @@ trait Implicits { /* If there is no winner, and we witnessed and caught divergence, * now we can throw it for the error message. */ - if (DivergentImplicitRecovery.sym != null) { - DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym, DivergentImplicitRecovery.explanation)(context) + DivergentImplicitRecovery.typeErrors match { + case err :: _ => context.issue(err) + case _ if DivergentImplicitRecovery.sym != null => + DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym, DivergentImplicitRecovery.explanation)(context) + case _ => } if (invalidImplicits.nonEmpty) diff --git a/test/files/neg/divergent-implicit.check b/test/files/neg/divergent-implicit.check index 60d876409f0d..9ba6351fea7b 100644 --- a/test/files/neg/divergent-implicit.check +++ b/test/files/neg/divergent-implicit.check @@ -3,6 +3,13 @@ divergent-implicit.scala:4: error: type mismatch; required: String val x1: String = 1 ^ +divergent-implicit.scala:5: error: diverging implicit expansion for type Int => String +starting with method $conforms in object Predef. + Test1.this.cast[Int, String](1) + Test1.this.cast[Int, Nothing](x) + Test1.this.cast[Int, Nothing](x) + val x2: String = cast[Int, String](1) + ^ divergent-implicit.scala:14: error: type mismatch; found : Test2.Foo required: Test2.Bar @@ -13,4 +20,4 @@ divergent-implicit.scala:15: error: type mismatch; required: Test2.Bar val y: Bar = new Baz ^ -three errors found +four errors found diff --git a/test/files/t8460.scala b/test/files/t8460.scala new file mode 100644 index 000000000000..10d2ed432ce6 --- /dev/null +++ b/test/files/t8460.scala @@ -0,0 +1,25 @@ +object tan extends UFunc { + implicit def ImplDouble: Impl[Double, Double] = ??? +} + +trait UFunc { + trait TC[+A] + type Impl[V, VR] = UFunc.UImpl[this.type, V, VR] +} + +object UFunc { + class UImpl[A, B, C] + implicit def implicitDoubleUTag[Tag, V, VR](implicit conv: V=>Double, impl: UImpl[Tag, Double, VR]):UImpl[Tag, V, VR] = ??? + +} + +object Test { + implicitly[tan.Impl[Double, Double]] + // we should discard the one and only divergent implicit (`implicitDoubleUTag`) + // This is done under `scalac-hash v2.10.4 test.scala`, but not under + // `scalac-hash v2.10.4 -Xdivergence211 test.scala` + // + // This seems to be because the companion implicits contain redundant entries + // + +} From 409a668e244dafa85eebce34b7d7a42037205cee Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 30 Mar 2014 21:44:45 +0200 Subject: [PATCH 3/7] refactor --- .../tools/nsc/typechecker/Implicits.scala | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 887008a30aff..072ffcf5833e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -844,24 +844,28 @@ trait Implicits { * so that if there is a best candidate it can still be selected. */ object DivergentImplicitRecovery { - // symbol of the implicit that caused the divergence. - // Initially null, will be saved on first diverging expansion. - private var implicitSym: Symbol = _ - private var countdown: Int = 1 - var explanation: String = "" - var typeErrors: List[DivergentImplicitTypeError] = Nil - - def sym: Symbol = implicitSym + private var divergentError: Option[DivergentImplicitTypeError] = None + private def saveDivergent(err: DivergentImplicitTypeError) { + if (divergentError.isEmpty) divergentError = Some(err) + } + + def flushErrorsSavingDivergent() { + val firstErr = context.reportBuffer.errors.collectFirst { + case err: DivergentImplicitTypeError => err + } + firstErr foreach saveDivergent + context.flushBuffer() + } + + def issueSavedDivergentError() { + divergentError foreach (err => context.issue(err)) + } + def apply(search: SearchResult, i: ImplicitInfo): SearchResult = - if (search.isDivergent && countdown > 0) { - countdown -= 1 - if (i.sym == implicitSym) - ??? - implicitSym = i.sym - explanation = search.explanation - typeErrors = context.errors.iterator.collect { case dite: DivergentImplicitTypeError => dite }.toList - context.flushBuffer() - log(s"discarding divergent implicit $implicitSym during implicit search") + if (search.isDivergent && divergentError.isEmpty) { + flushErrorsSavingDivergent() + saveDivergent(DivergentImplicitTypeError(tree, pt, i.sym, search.explanation)) + log(s"discarding divergent implicit ${i.sym} during implicit search") SearchFailure } else search } @@ -898,11 +902,7 @@ trait Implicits { case sr if sr.isFailure => // We don't want errors that occur during checking implicit info // to influence the check of further infos. - val divergents = context.reportBuffer.errors.collect { - case err: DivergentImplicitTypeError => err - } - DivergentImplicitRecovery.typeErrors = divergents.toList - context.reportBuffer.clearAll() + DivergentImplicitRecovery.flushErrorsSavingDivergent() rankImplicits(is, acc) case newBest => best = newBest @@ -945,15 +945,9 @@ trait Implicits { } if (best.isFailure) { - /* If there is no winner, and we witnessed and caught divergence, - * now we can throw it for the error message. - */ - DivergentImplicitRecovery.typeErrors match { - case err :: _ => context.issue(err) - case _ if DivergentImplicitRecovery.sym != null => - DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym, DivergentImplicitRecovery.explanation)(context) - case _ => - } + // If there is no winner, and we witnessed and recoreded a divergence error, + // our recovery attempt has failed, so we must now issue it. + DivergentImplicitRecovery.issueSavedDivergentError() if (invalidImplicits.nonEmpty) setAddendum(pos, () => From 0dae042c2b049f24e3dbee3fbc6cf0b09ce69f5f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 30 Mar 2014 21:47:23 +0200 Subject: [PATCH 4/7] refactor --- src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 072ffcf5833e..b86cd1309277 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -453,10 +453,6 @@ trait Implicits { case (OpenImplicit(info, pt, tree), i) => s"${" " * i} $tree" }.mkString("\n") - //println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG -// context.map { -// case OpenImplicit() -// } new DivergentSearchFailure(msg) case None => try { From 8ad318264e5c55e47eebb15e1d979165d4ef6bee Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 30 Mar 2014 21:47:52 +0200 Subject: [PATCH 5/7] whitespace --- src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index b86cd1309277..f866f6b03fd7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -852,7 +852,7 @@ trait Implicits { firstErr foreach saveDivergent context.flushBuffer() } - + def issueSavedDivergentError() { divergentError foreach (err => context.issue(err)) } From 36a23bf2bfa59c59c03b756b5e90f02df9baf057 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 30 Mar 2014 21:57:48 +0200 Subject: [PATCH 6/7] refactor --- .../tools/nsc/typechecker/Implicits.scala | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index f866f6b03fd7..3442fb7854c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -841,29 +841,30 @@ trait Implicits { */ object DivergentImplicitRecovery { private var divergentError: Option[DivergentImplicitTypeError] = None + private def saveDivergent(err: DivergentImplicitTypeError) { if (divergentError.isEmpty) divergentError = Some(err) } - def flushErrorsSavingDivergent() { - val firstErr = context.reportBuffer.errors.collectFirst { - case err: DivergentImplicitTypeError => err - } - firstErr foreach saveDivergent - context.flushBuffer() - } - def issueSavedDivergentError() { divergentError foreach (err => context.issue(err)) } - def apply(search: SearchResult, i: ImplicitInfo): SearchResult = + def apply(search: SearchResult, i: ImplicitInfo, errors: Seq[AbsTypeError]): SearchResult = { + val firstErr = errors.collectFirst { + case err: DivergentImplicitTypeError => err + } + firstErr foreach saveDivergent + if (search.isDivergent && divergentError.isEmpty) { - flushErrorsSavingDivergent() saveDivergent(DivergentImplicitTypeError(tree, pt, i.sym, search.explanation)) log(s"discarding divergent implicit ${i.sym} during implicit search") SearchFailure - } else search + } else { + context.flushBuffer() + search + } + } } /** Sorted list of eligible implicits. @@ -890,15 +891,16 @@ trait Implicits { case Nil => acc case i :: is => val typedImplicitResult = typedImplicit(i, ptChecked = true, isLocalToCallsite) - val depth = context.openImplicits.length - val recoveredResult = DivergentImplicitRecovery(typedImplicitResult , i) + // We don't want errors that occur during checking implicit info + // to influence the check of further infos. But we do need to pass the errors + // to `DivergentImplicitRecovery` so that it can note `DivergentImplicitTypeError` that is + // being propagated from a nested implicit search; this will be re-issued if this level + // of the search fails. + val recoveredResult = DivergentImplicitRecovery(typedImplicitResult, i, context.flushAndReturnBuffer()) recoveredResult match { case sr if sr.isDivergent => Nil case sr if sr.isFailure => - // We don't want errors that occur during checking implicit info - // to influence the check of further infos. - DivergentImplicitRecovery.flushErrorsSavingDivergent() rankImplicits(is, acc) case newBest => best = newBest From d80199937dbefdefbab653d2288fc9814b59fdf8 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 30 Mar 2014 22:04:38 +0200 Subject: [PATCH 7/7] refactor --- .../scala/tools/nsc/typechecker/Implicits.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 3442fb7854c9..d8aeffa26420 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -851,19 +851,18 @@ trait Implicits { } def apply(search: SearchResult, i: ImplicitInfo, errors: Seq[AbsTypeError]): SearchResult = { - val firstErr = errors.collectFirst { - case err: DivergentImplicitTypeError => err - } - firstErr foreach saveDivergent + // A divergent error from a nested implicit search will be found in `errors`. Stash that + // aside to be re-issued if this implicit search fails. + errors.collectFirst { case err: DivergentImplicitTypeError => err } foreach saveDivergent if (search.isDivergent && divergentError.isEmpty) { + // Divergence triggered by `i` at this level of the implicit serach. We haven't + // seen divergence so far, we won't issue this error just yet, and instead temporarily + // treat `i` as a failed candidate. saveDivergent(DivergentImplicitTypeError(tree, pt, i.sym, search.explanation)) log(s"discarding divergent implicit ${i.sym} during implicit search") SearchFailure - } else { - context.flushBuffer() - search - } + } else search } }