Skip to content

Commit 1b21784

Browse files
committed
Merge pull request scala#3669 from adriaanm/rebase-3667
SI-8460 Fix regression in implicit divergence recovery
2 parents 8489be1 + afccae6 commit 1b21784

File tree

3 files changed

+92
-42
lines changed

3 files changed

+92
-42
lines changed

src/compiler/scala/tools/nsc/typechecker/Implicits.scala

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -833,19 +833,41 @@ trait Implicits {
833833
* so that if there is a best candidate it can still be selected.
834834
*/
835835
object DivergentImplicitRecovery {
836-
// symbol of the implicit that caused the divergence.
837-
// Initially null, will be saved on first diverging expansion.
838-
private var implicitSym: Symbol = _
839-
private var countdown: Int = 1
840-
841-
def sym: Symbol = implicitSym
842-
def apply(search: SearchResult, i: ImplicitInfo): SearchResult =
843-
if (search.isDivergent && countdown > 0) {
844-
countdown -= 1
845-
implicitSym = i.sym
846-
log(s"discarding divergent implicit $implicitSym during implicit search")
836+
private var divergentError: Option[DivergentImplicitTypeError] = None
837+
838+
private def saveDivergent(err: DivergentImplicitTypeError) {
839+
if (divergentError.isEmpty) divergentError = Some(err)
840+
}
841+
842+
def issueSavedDivergentError() {
843+
divergentError foreach (err => context.issue(err))
844+
}
845+
846+
def apply(search: SearchResult, i: ImplicitInfo, errors: Seq[AbsTypeError]): SearchResult = {
847+
// A divergent error from a nested implicit search will be found in `errors`. Stash that
848+
// aside to be re-issued if this implicit search fails.
849+
errors.collectFirst { case err: DivergentImplicitTypeError => err } foreach saveDivergent
850+
851+
if (search.isDivergent && divergentError.isEmpty) {
852+
// Divergence triggered by `i` at this level of the implicit serach. We haven't
853+
// seen divergence so far, we won't issue this error just yet, and instead temporarily
854+
// treat `i` as a failed candidate.
855+
saveDivergent(DivergentImplicitTypeError(tree, pt, i.sym))
856+
log(s"discarding divergent implicit ${i.sym} during implicit search")
847857
SearchFailure
848-
} else search
858+
} else {
859+
if (search.isFailure) {
860+
// We don't want errors that occur during checking implicit info
861+
// to influence the check of further infos, but we should retain divergent implicit errors
862+
// (except for the one we already squirreled away)
863+
val saved = divergentError.getOrElse(null)
864+
context.reportBuffer.retainErrors {
865+
case err: DivergentImplicitTypeError => err ne saved
866+
}
867+
}
868+
search
869+
}
870+
}
849871
}
850872

851873
/** Sorted list of eligible implicits.
@@ -869,31 +891,33 @@ trait Implicits {
869891
* - if it matches, forget about all others it improves upon
870892
*/
871893
@tailrec private def rankImplicits(pending: Infos, acc: Infos): Infos = pending match {
872-
case Nil => acc
873-
case i :: is =>
874-
DivergentImplicitRecovery(typedImplicit(i, ptChecked = true, isLocalToCallsite), i) match {
875-
case sr if sr.isDivergent =>
876-
Nil
877-
case sr if sr.isFailure =>
878-
// We don't want errors that occur during checking implicit info
879-
// to influence the check of further infos.
880-
context.reportBuffer.retainErrors {
881-
case err: DivergentImplicitTypeError => true
894+
case Nil => acc
895+
case firstPending :: otherPending =>
896+
def firstPendingImproves(alt: ImplicitInfo) =
897+
firstPending == alt || (
898+
try improves(firstPending, alt)
899+
catch {
900+
case e: CyclicReference =>
901+
debugwarn(s"Discarding $firstPending during implicit search due to cyclic reference.")
902+
true
882903
}
883-
rankImplicits(is, acc)
884-
case newBest =>
885-
best = newBest
886-
val newPending = undoLog undo {
887-
is filterNot (alt => alt == i || {
888-
try improves(i, alt)
889-
catch {
890-
case e: CyclicReference =>
891-
debugwarn(s"Discarding $i during implicit search due to cyclic reference")
892-
true
893-
}
894-
})
904+
)
905+
906+
val typedFirstPending = typedImplicit(firstPending, ptChecked = true, isLocalToCallsite)
907+
908+
// Pass the errors to `DivergentImplicitRecovery` so that it can note
909+
// the first `DivergentImplicitTypeError` that is being propagated
910+
// from a nested implicit search; this one will be
911+
// re-issued if this level of the search fails.
912+
DivergentImplicitRecovery(typedFirstPending, firstPending, context.errors) match {
913+
case sr if sr.isDivergent => Nil
914+
case sr if sr.isFailure => rankImplicits(otherPending, acc)
915+
case newBest =>
916+
best = newBest // firstPending is our new best, since we already pruned last time around:
917+
val pendingImprovingBest = undoLog undo {
918+
otherPending filterNot firstPendingImproves
895919
}
896-
rankImplicits(newPending, i :: acc)
920+
rankImplicits(pendingImprovingBest, firstPending :: acc)
897921
}
898922
}
899923

@@ -921,12 +945,9 @@ trait Implicits {
921945
}
922946

923947
if (best.isFailure) {
924-
/* If there is no winner, and we witnessed and caught divergence,
925-
* now we can throw it for the error message.
926-
*/
927-
if (DivergentImplicitRecovery.sym != null) {
928-
DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym)(context)
929-
}
948+
// If there is no winner, and we witnessed and recorded a divergence error,
949+
// our recovery attempt has failed, so we must now issue it.
950+
DivergentImplicitRecovery.issueSavedDivergentError()
930951

931952
if (invalidImplicits.nonEmpty)
932953
setAddendum(pos, () =>

test/files/neg/divergent-implicit.check

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ divergent-implicit.scala:4: error: type mismatch;
33
required: String
44
val x1: String = 1
55
^
6+
divergent-implicit.scala:5: error: diverging implicit expansion for type Int => String
7+
starting with method $conforms in object Predef
8+
val x2: String = cast[Int, String](1)
9+
^
610
divergent-implicit.scala:14: error: type mismatch;
711
found : Test2.Foo
812
required: Test2.Bar
@@ -13,4 +17,4 @@ divergent-implicit.scala:15: error: type mismatch;
1317
required: Test2.Bar
1418
val y: Bar = new Baz
1519
^
16-
three errors found
20+
four errors found

test/files/pos/t8460.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
object tan extends UFunc {
2+
implicit def ImplDouble: Impl[Double, Double] = ???
3+
}
4+
5+
trait UFunc {
6+
trait TC[+A]
7+
type Impl[V, VR] = UFunc.UImpl[this.type, V, VR]
8+
}
9+
10+
object UFunc {
11+
class UImpl[A, B, C]
12+
implicit def implicitDoubleUTag[Tag, V, VR](implicit conv: V=>Double, impl: UImpl[Tag, Double, VR]):UImpl[Tag, V, VR] = ???
13+
14+
}
15+
16+
object Test {
17+
implicitly[tan.Impl[Double, Double]]
18+
// we should discard the one and only divergent implicit (`implicitDoubleUTag`)
19+
// This is done under `scalac-hash v2.10.4 test.scala`, but not under
20+
// `scalac-hash v2.10.4 -Xdivergence211 test.scala`
21+
//
22+
// This seems to be because the companion implicits contain redundant entries
23+
//
24+
25+
}

0 commit comments

Comments
 (0)