summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala39
-rw-r--r--src/compiler/scala/tools/nsc/util/Exceptional.scala14
-rw-r--r--test/files/pos/bug3883.scala15
3 files changed, 60 insertions, 8 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 655e5dfe09..3fc8344d8d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -591,7 +591,7 @@ trait Implicits {
* If it is null, no shadowing.
*/
class ImplicitComputation(iss: Infoss, shadowed: util.HashSet[Name]) {
- private var _best: SearchResult = SearchFailure
+ private var best: SearchResult = SearchFailure
/** True if a given ImplicitInfo (already known isValid) is eligible.
*/
@@ -612,6 +612,18 @@ trait Implicits {
*/
private def checkValid(sym: Symbol) = isValid(sym) || { invalidImplicits += sym ; false }
+ /** Preventing a divergent implicit from terminating implicit search,
+ * so that if there is a best candidate it can still be selected.
+ */
+ private var divergence = false
+ private val MaxDiverges = 1 // not sure if this should be > 1
+ private val divergenceHandler = util.Exceptional.expiringHandler(MaxDiverges) {
+ case x: DivergentImplicit =>
+ divergence = true
+ log("discarding divergent implicit during implicit search")
+ SearchFailure
+ }
+
/** Sorted list of eligible implicits.
*/
val eligible = {
@@ -635,10 +647,14 @@ trait Implicits {
@tailrec private def rankImplicits(pending: Infos, acc: Infos): Infos = pending match {
case Nil => acc
case i :: is =>
- typedImplicit(i, true) match {
+ def tryImplicitInfo(i: ImplicitInfo) =
+ try typedImplicit(i, true)
+ catch divergenceHandler
+
+ tryImplicitInfo(i) match {
case SearchFailure => rankImplicits(is, acc)
case newBest =>
- _best = newBest
+ best = newBest
val newPending = undoLog undo {
is filterNot (alt => alt == i || {
try improves(i, alt)
@@ -671,13 +687,20 @@ trait Implicits {
}
}
- if (_best == SearchFailure && invalidImplicits.nonEmpty) {
- setAddendum(tree.pos, () =>
- "\n Note: implicit "+invalidImplicits.head+" is not applicable here"+
- " because it comes after the application point and it lacks an explicit result type")
+ if (best == SearchFailure) {
+ /** If there is no winner, and we witnessed and caught divergence,
+ * now we can throw it for the error message.
+ */
+ if (divergence)
+ throw DivergentImplicit
+
+ if (invalidImplicits.nonEmpty)
+ setAddendum(tree.pos, () =>
+ "\n Note: implicit "+invalidImplicits.head+" is not applicable here"+
+ " because it comes after the application point and it lacks an explicit result type")
}
- _best
+ best
}
}
diff --git a/src/compiler/scala/tools/nsc/util/Exceptional.scala b/src/compiler/scala/tools/nsc/util/Exceptional.scala
index 4230f29eb5..459881bb64 100644
--- a/src/compiler/scala/tools/nsc/util/Exceptional.scala
+++ b/src/compiler/scala/tools/nsc/util/Exceptional.scala
@@ -64,6 +64,20 @@ class Exceptional(val ex: Throwable)(implicit prefs: ScalaPrefs) {
object Exceptional {
+ type Catcher[+T] = PartialFunction[Throwable, T]
+
+ /** Creates an exception handler which will only ever catch the given
+ * number of exceptions (if the given pf is defined there) and after
+ * that will disable itself.
+ */
+ def expiringHandler[T](numCatches: Int)(pf: Catcher[T]): Catcher[T] = {
+ var remaining = numCatches;
+ { case ex: Throwable if remaining > 0 && pf.isDefinedAt(ex) =>
+ remaining -= 1
+ pf(ex)
+ }
+ }
+
/** The Throwable => Exceptional implicit plus the associated factory. */
implicit def throwableToExceptional(ex: Throwable)(implicit prefs: ScalaPrefs): Exceptional = apply(ex)(prefs)
def apply(ex: Throwable)(implicit prefs: ScalaPrefs) = new Exceptional(ex)(prefs)
diff --git a/test/files/pos/bug3883.scala b/test/files/pos/bug3883.scala
new file mode 100644
index 0000000000..adde0526b2
--- /dev/null
+++ b/test/files/pos/bug3883.scala
@@ -0,0 +1,15 @@
+// need to test both orders
+object A1 {
+ implicit def i: Equiv[Boolean] = error("")
+ implicit def div[T, A](implicit f: T => A, eq: Equiv[A]): Equiv[T] = error("")
+
+ implicitly[Equiv[Boolean]]
+}
+
+object A2 {
+ implicit def div[T, A](implicit f: T => A, eq: Equiv[A]): Equiv[T] = error("")
+ implicit def i: Equiv[Boolean] = error("")
+
+ implicitly[Equiv[Boolean]]
+}
+