aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala13
-rw-r--r--tests/pos/implicit-scope-loop.scala17
2 files changed, 29 insertions, 1 deletions
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala
index 38ac709be..b3a1010cb 100644
--- a/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/src/dotty/tools/dotc/typer/Implicits.scala
@@ -363,7 +363,18 @@ trait ImplicitRunInfo { self: RunInfo =>
computeIScope(cacheResult = false)
else implicitScopeCache get tp match {
case Some(is) => is
- case None => computeIScope(cacheResult = seen.isEmpty)
+ case None =>
+ // Implicit scopes are tricky to cache because of loops. For example
+ // in `tests/pos/implicit-scope-loop.scala`, the scope of B contains
+ // the scope of A which contains the scope of B. We break the loop
+ // by returning EmptyTermRefSet in `collectCompanions` for types
+ // that we have already seen, but this means that we cannot cache
+ // the computed scope of A, it is incomplete.
+ // Keeping track of exactly where these loops happen would require a
+ // lot of book-keeping, instead we choose to be conservative and only
+ // cache scopes before any type has been seen. This is unfortunate
+ // because loops are very common for types in scala.collection.
+ computeIScope(cacheResult = seen.isEmpty)
}
}
diff --git a/tests/pos/implicit-scope-loop.scala b/tests/pos/implicit-scope-loop.scala
new file mode 100644
index 000000000..162f1a52c
--- /dev/null
+++ b/tests/pos/implicit-scope-loop.scala
@@ -0,0 +1,17 @@
+trait Dummy[T]
+
+
+trait A[T] extends B
+trait B extends Dummy[A[Int]]
+object B {
+ implicit def theB: B = new B {}
+ implicit def theA: A[Int] = new A[Int] {}
+}
+
+object Test {
+ def getB(implicit b: B) = b
+ def getA[T](implicit a: A[T]) = a
+
+ getB
+ getA
+} \ No newline at end of file