diff options
author | Geoffrey Washburn <geoffrey.washburn@epfl.ch> | 2008-09-05 14:25:05 +0000 |
---|---|---|
committer | Geoffrey Washburn <geoffrey.washburn@epfl.ch> | 2008-09-05 14:25:05 +0000 |
commit | fa8d0d8d853cebdfa33552ca2f66c229b6d39f2d (patch) | |
tree | a626126927ae5f7c1933689bdfcf290d239064fe | |
parent | d0eb6ae1a2a6136ed90d7cbab0efcacc4cd4c337 (diff) | |
download | scala-fa8d0d8d853cebdfa33552ca2f66c229b6d39f2d.tar.gz scala-fa8d0d8d853cebdfa33552ca2f66c229b6d39f2d.tar.bz2 scala-fa8d0d8d853cebdfa33552ca2f66c229b6d39f2d.zip |
Added support for -Yrecursion compiler flag.
Added two tests involving this flag.
-rw-r--r-- | src/compiler/scala/tools/nsc/Settings.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Symbols.scala | 56 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Types.scala | 8 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 10 | ||||
-rw-r--r-- | test/files/pos/comp-rec-test.flags | 1 | ||||
-rw-r--r-- | test/files/pos/comp-rec-test.scala | 24 | ||||
-rw-r--r-- | test/files/pos/proj-rec-test.flags | 1 | ||||
-rw-r--r-- | test/files/pos/proj-rec-test.scala | 13 |
8 files changed, 99 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index 3b1a3cff21..8425e49043 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -141,6 +141,7 @@ class Settings(error: String => Unit) { val logAll = BooleanSetting ("-Ylog-all", "Log all operations").hideToIDE val noimports = BooleanSetting ("-Yno-imports", "Compile without any implicit imports") val nopredefs = BooleanSetting ("-Yno-predefs", "Compile without any implicit predefined values") + val Yrecursion = IntSetting ("-Yrecursion", "Recursion depth used when locking symbols", 0, Some(0), None) val script = StringSetting ("-Xscript", "object", "Compile as a script, wrapping the code into object.main()", "").hideToIDE val Xshowtrees = BooleanSetting ("-Yshow-trees", "Show detailed trees when used in connection with -print:phase").hideToIDE diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index 3365555ed6..f01b274d39 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -7,6 +7,7 @@ package scala.tools.nsc.symtab import scala.collection.mutable.ListBuffer +import scala.collection.immutable.Map import scala.tools.nsc.io.AbstractFile import scala.tools.nsc.util.{Position, NoPosition, BatchSourceFile} import Flags._ @@ -26,6 +27,11 @@ trait Symbols { val emptySymbolArray = new Array[Symbol](0) val emptySymbolSet = Set.empty[Symbol] + + + /** Used to keep track of the recursion depth on locked symbols */ + private var recursionTable = Map.empty[Symbol, Int] + /* type Position; def NoPos : Position; @@ -181,6 +187,44 @@ trait Symbols { final def newErrorSymbol(name: Name): Symbol = if (name.isTypeName) newErrorClass(name) else newErrorValue(name) +// Locking and unlocking ------------------------------------------------------ + + // True if the symbol is unlocked. + // True if the symbol is locked but still below the allowed recursion depth. + // False otherwise + def lockOK: Boolean = { + ((rawflags & LOCKED) == 0) || + ((settings.Yrecursion.value != 0) && + (recursionTable get this match { + case Some(n) => (n <= settings.Yrecursion.value) + case None => true })) + } + + // Lock a symbol, using the handler if the recursion depth becomes too great. + def lock(handler: => Unit) = { + if ((rawflags & LOCKED) != 0) { + if (settings.Yrecursion.value != 0) { + recursionTable get this match { + case Some(n) => + if (n > settings.Yrecursion.value) { + handler + } else { + recursionTable += (this -> (n + 1)) + } + case None => + recursionTable += (this -> 1) + } + } else { handler } + } else { rawflags |= LOCKED } + } + + // Unlock a symbol + def unlock() = { + rawflags = rawflags & ~LOCKED + if (settings.Yrecursion.value != 0) + recursionTable -= this + } + // Tests ---------------------------------------------------------------------- def isTerm = false //to be overridden @@ -499,17 +543,16 @@ trait Symbols { assert(infos.prev eq null, this.name) val tp = infos.info //if (settings.debug.value) System.out.println("completing " + this.rawname + tp.getClass());//debug - if ((rawflags & LOCKED) != 0) { + lock { setInfo(ErrorType) throw CyclicReference(this, tp) } - rawflags = rawflags | LOCKED val current = phase try { phase = phaseOf(infos.validFrom) tp.complete(this) // if (settings.debug.value && runId(validTo) == currentRunId) System.out.println("completed " + this/* + ":" + info*/);//DEBUG - rawflags = rawflags & ~LOCKED + unlock() } finally { phase = current } @@ -531,10 +574,10 @@ trait Symbols { assert(info ne null) infos = TypeHistory(currentPeriod, info, null) if (info.isComplete) { - rawflags = rawflags & ~LOCKED + unlock() validTo = currentPeriod } else { - rawflags = rawflags & ~LOCKED + unlock() validTo = NoPeriod } this @@ -1597,7 +1640,8 @@ trait Symbols { privateWithin = this override def setInfo(info: Type): this.type = { infos = TypeHistory(1, NoType, null) - rawflags = rawflags & ~ LOCKED + unlock() +// rawflags = rawflags & ~ LOCKED validTo = currentPeriod this } diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 5c6f9add84..aaa24b7d19 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -2051,11 +2051,11 @@ A type's typeSymbol should never be inspected directly. if (sym1.isAliasType && sym1.info.typeParams.length == args.length) { // note: we require that object is initialized, // that's why we use info.typeParams instead of typeParams. - if (sym1.hasFlag(LOCKED)) + sym1.lock { throw new TypeError("illegal cyclic reference involving " + sym1) - sym1.setFlag(LOCKED) - transform(sym1.info) // check there are no cycles - sym1.resetFlag(LOCKED) + } + transform(sym1.info) // check there are no cycles + sym1.unlock() rawTypeRef(pre, sym1, args) // don't expand type alias (cycles checked above) } else { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ea7f9d447d..592d95fd2e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -301,9 +301,7 @@ trait Typers { self: Analyzer => def checkNonCyclic(pos: Position, tp: Type): Boolean = { def checkNotLocked(sym: Symbol): Boolean = { sym.initialize - if (sym hasFlag LOCKED) { - error(pos, "cyclic aliasing or subtyping involving "+sym); false - } else true + sym.lockOK || {error(pos, "cyclic aliasing or subtyping involving "+sym); false} } tp match { case TypeRef(pre, sym, args) => @@ -332,9 +330,11 @@ trait Typers { self: Analyzer => } def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Boolean = { - lockedSym.setFlag(LOCKED) + lockedSym.lock { + throw new TypeError("illegal cyclic reference involving " + lockedSym) + } val result = checkNonCyclic(pos, tp) - lockedSym.resetFlag(LOCKED) + lockedSym.unlock() result } diff --git a/test/files/pos/comp-rec-test.flags b/test/files/pos/comp-rec-test.flags new file mode 100644 index 0000000000..ad928f52a0 --- /dev/null +++ b/test/files/pos/comp-rec-test.flags @@ -0,0 +1 @@ +-Yrecursion 1 diff --git a/test/files/pos/comp-rec-test.scala b/test/files/pos/comp-rec-test.scala new file mode 100644 index 0000000000..eaf12942c7 --- /dev/null +++ b/test/files/pos/comp-rec-test.scala @@ -0,0 +1,24 @@ +object Comp extends Application { + + trait Family { + type T + } + + object Trivial extends Family { + type T = Unit + } + + trait Wrap extends Family { + val v : Family + type T = v.T + } + + object WrapTrivial extends Wrap { + val v = Trivial + } + + object WrapWrapTrivial extends Wrap { + val v = WrapTrivial + } + +} diff --git a/test/files/pos/proj-rec-test.flags b/test/files/pos/proj-rec-test.flags new file mode 100644 index 0000000000..ad928f52a0 --- /dev/null +++ b/test/files/pos/proj-rec-test.flags @@ -0,0 +1 @@ +-Yrecursion 1 diff --git a/test/files/pos/proj-rec-test.scala b/test/files/pos/proj-rec-test.scala new file mode 100644 index 0000000000..b7efcf3e8d --- /dev/null +++ b/test/files/pos/proj-rec-test.scala @@ -0,0 +1,13 @@ +object ProjTest { + trait MInt { type Type } + trait _0 extends MInt { type Type = Boolean } + trait Succ[Pre <: MInt] extends MInt { type Type = Pre#Type } + + type _1 = Succ[_0] + type _2 = Succ[_1] + + type X1 = _0#Type // Ok + type X2 = _1#Type // Ok + type X3 = _2#Type // Compiler error, illegal cyclic reference involving type Type +} + |