diff options
author | Paul Phillips <paulp@improving.org> | 2012-09-28 14:36:42 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-10-09 14:17:34 -0700 |
commit | ba36c44c31d1a1e0b5c0cf3d4775edd0ae0d5a13 (patch) | |
tree | 8eb8caeb82afa0677a2e78291a990a9e525f3c7e | |
parent | 432f9368011e0fd9e89ca0e18082bfec180baf32 (diff) | |
download | scala-ba36c44c31d1a1e0b5c0cf3d4775edd0ae0d5a13.tar.gz scala-ba36c44c31d1a1e0b5c0cf3d4775edd0ae0d5a13.tar.bz2 scala-ba36c44c31d1a1e0b5c0cf3d4775edd0ae0d5a13.zip |
Fix for SI-4744, another variety of cycle.
I threw this in with the previous commit behind -Ybreak-cycles, but
this one is much less sketchy. Explanation: have to handle f-bounds
more deftly. Namers forces lower bounds to prevent recursion in
that direction, but a light touch is required to handle these two
situations differently:
// This is a cyclic type parameter - an error is correct
class A[T <: Comparable[_ <: T]]
// This is not cyclic - it flips the arrow
class B[T <: Comparable[_ >: T]]
Long have I been haunted by the knowledge that you can
write class B in java, but not in scala:
public class B<T extends Comparable<? super T>> {}
It's over! We've achieved parity with java.
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 40 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Symbols.scala | 6 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 3 | ||||
-rw-r--r-- | test/files/neg/cycle-bounds.check | 4 | ||||
-rw-r--r-- | test/files/neg/cycle-bounds.flags | 1 | ||||
-rw-r--r-- | test/files/neg/cycle-bounds.scala | 5 | ||||
-rw-r--r-- | test/files/neg/t1224.check | 2 | ||||
-rw-r--r-- | test/files/neg/t1224.flags | 1 | ||||
-rw-r--r-- | test/files/pos/cycle-bounds.scala | 1 | ||||
-rw-r--r-- | test/files/pos/t4744.flags | 1 | ||||
-rw-r--r-- | test/files/pos/t4744/Bar.scala (renamed from test/pending/pos/t4744/Bar.scala) | 0 | ||||
-rw-r--r-- | test/files/pos/t4744/Foo.java (renamed from test/pending/pos/t4744/Foo.java) | 0 | ||||
-rw-r--r-- | wip.scala | 2 |
14 files changed, 51 insertions, 17 deletions
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 4829fb81b5..404f5e6b6e 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -127,7 +127,7 @@ trait ScalaSettings extends AbsScalaSettings val overrideObjects = BooleanSetting ("-Yoverride-objects", "Allow member objects to be overridden.") val overrideVars = BooleanSetting ("-Yoverride-vars", "Allow vars to be overridden.") val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options.") - val breakCycles = BooleanSetting ("-Ybreak-cycles", "Attempt to break cycles encountered during classfile parsing") + val breakCycles = BooleanSetting ("-Ybreak-cycles", "Attempt to break cycles encountered during typing") val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after") val check = PhasesSetting ("-Ycheck", "Check the tree at the end of") val Yshow = PhasesSetting ("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after") diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 4b60fd8b27..710b3e4e54 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -707,30 +707,50 @@ trait Namers extends MethodSynthesis { // --- Lazy Type Assignment -------------------------------------------------- - def initializeLowerBounds(tp: Type): Type = { + def findCyclicalLowerBound(tp: Type): Symbol = { tp match { case TypeBounds(lo, _) => // check that lower bound is not an F-bound // but carefully: class Foo[T <: Bar[_ >: T]] should be allowed - for (TypeRef(_, sym, _) <- lo) - sym.maybeInitialize + for (tp1 @ TypeRef(_, sym, _) <- lo) { + if (settings.breakCycles.value) { + if (!sym.maybeInitialize) { + log(s"Cycle inspecting $lo for possible f-bounds: ${sym.fullLocationString}") + return sym + } + } + else sym.initialize + } case _ => } - tp + NoSymbol } def monoTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => + // this early test is there to avoid infinite baseTypes when + // adding setters and getters --> bug798 + def needsCycleCheck = sym.isNonClassType && !sym.isParameter && !sym.isExistential logAndValidate(sym) { - val tp = initializeLowerBounds(typeSig(tree)) + val tp = typeSig(tree) + + findCyclicalLowerBound(tp) andAlso { sym => + if (needsCycleCheck) { + // neg/t1224: trait C[T] ; trait A { type T >: C[T] <: C[C[T]] } + // To avoid an infinite loop on the above, we cannot break all cycles + log(s"Reinitializing info of $sym to catch any genuine cycles") + sym reset sym.info + sym.initialize + } + } sym setInfo { if (sym.isJavaDefined) RestrictJavaArraysMap(tp) else tp } - // this early test is there to avoid infinite baseTypes when - // adding setters and getters --> bug798 - val needsCycleCheck = (sym.isAliasType || sym.isAbstractType) && !sym.isParameter - if (needsCycleCheck && !typer.checkNonCyclic(tree.pos, tp)) - sym setInfo ErrorType + if (needsCycleCheck) { + log(s"Needs cycle check: ${sym.debugLocationString}") + if (!typer.checkNonCyclic(tree.pos, tp)) + sym setInfo ErrorType + } } } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d506a43829..7d0c05bc81 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1398,9 +1398,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!isInitialized) info this } - def maybeInitialize: this.type = { - try initialize - catch { case _: CyclicReference => debuglog("Encountering cycle in maybe-initialization of $this") ; this } + def maybeInitialize = { + try { initialize ; true } + catch { case _: CyclicReference => debuglog("Hit cycle in maybeInitialize of $this") ; false } } /** Called when the programmer requests information that might require initialization of the underlying symbol. diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 9a43ad441f..f8b5d089e8 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1710,7 +1710,8 @@ trait Types extends api.Types { self: SymbolTable => object baseClassesCycleMonitor { private var open: List[Symbol] = Nil @inline private def cycleLog(msg: => String) { - Console.err.println(msg) + if (settings.debug.value) + Console.err.println(msg) } def size = open.size def push(clazz: Symbol) { diff --git a/test/files/neg/cycle-bounds.check b/test/files/neg/cycle-bounds.check new file mode 100644 index 0000000000..d924838aec --- /dev/null +++ b/test/files/neg/cycle-bounds.check @@ -0,0 +1,4 @@ +cycle-bounds.scala:5: error: illegal cyclic reference involving type T +class NotOk[T <: Comparable[_ <: T]] + ^ +one error found diff --git a/test/files/neg/cycle-bounds.flags b/test/files/neg/cycle-bounds.flags new file mode 100644 index 0000000000..ca20f55172 --- /dev/null +++ b/test/files/neg/cycle-bounds.flags @@ -0,0 +1 @@ +-Ybreak-cycles diff --git a/test/files/neg/cycle-bounds.scala b/test/files/neg/cycle-bounds.scala new file mode 100644 index 0000000000..0b43bc703e --- /dev/null +++ b/test/files/neg/cycle-bounds.scala @@ -0,0 +1,5 @@ +// This should be allowed +class Ok[T <: Comparable[_ >: T]] + +// This is (il)legitimately a cyclic reference +class NotOk[T <: Comparable[_ <: T]] diff --git a/test/files/neg/t1224.check b/test/files/neg/t1224.check index fb61275911..ab8a6f1130 100644 --- a/test/files/neg/t1224.check +++ b/test/files/neg/t1224.check @@ -1,4 +1,4 @@ -t1224.scala:4: error: illegal cyclic reference involving type T +t1224.scala:4: error: lower bound C[A.this.T] does not conform to upper bound C[C[A.this.T]] type T >: C[T] <: C[C[T]] ^ one error found diff --git a/test/files/neg/t1224.flags b/test/files/neg/t1224.flags new file mode 100644 index 0000000000..ca20f55172 --- /dev/null +++ b/test/files/neg/t1224.flags @@ -0,0 +1 @@ +-Ybreak-cycles diff --git a/test/files/pos/cycle-bounds.scala b/test/files/pos/cycle-bounds.scala deleted file mode 100644 index 0aa7aa552b..0000000000 --- a/test/files/pos/cycle-bounds.scala +++ /dev/null @@ -1 +0,0 @@ -class Foo[T <: Comparable[_ >: T]] diff --git a/test/files/pos/t4744.flags b/test/files/pos/t4744.flags new file mode 100644 index 0000000000..ca20f55172 --- /dev/null +++ b/test/files/pos/t4744.flags @@ -0,0 +1 @@ +-Ybreak-cycles diff --git a/test/pending/pos/t4744/Bar.scala b/test/files/pos/t4744/Bar.scala index 1fb6d78973..1fb6d78973 100644 --- a/test/pending/pos/t4744/Bar.scala +++ b/test/files/pos/t4744/Bar.scala diff --git a/test/pending/pos/t4744/Foo.java b/test/files/pos/t4744/Foo.java index 6c764d0470..6c764d0470 100644 --- a/test/pending/pos/t4744/Foo.java +++ b/test/files/pos/t4744/Foo.java diff --git a/wip.scala b/wip.scala new file mode 100644 index 0000000000..ed9ba97640 --- /dev/null +++ b/wip.scala @@ -0,0 +1,2 @@ +object Foo { } +case class Foo(x: Int) |