summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala50
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala44
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala138
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala358
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/ICodes.scala47
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala30
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Printers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala67
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala25
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala23
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala8
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala22
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala53
-rw-r--r--test/files/pos/simple-exceptions.scala12
20 files changed, 492 insertions, 406 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
index c09851fa86..45d1916ca9 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
@@ -256,7 +256,7 @@ trait BasicBlocks {
* put into ignore mode so we hear about it if there's a problem.
*/
instr match {
- case JUMP(_) | RETURN(_) | THROW() | SCOPE_EXIT(_) => // ok
+ case JUMP(_) | RETURN(_) | THROW(_) | SCOPE_EXIT(_) => // ok
case STORE_LOCAL(local) if isExceptionResultName(local.sym.name) => // ok
case x => log("Ignoring instruction, possibly at our peril, at " + pos + ": " + x)
}
@@ -373,7 +373,7 @@ trait BasicBlocks {
case CZJUMP(succ, fail, _, _) => fail :: succ :: Nil
case SWITCH(_, labels) => labels
case RETURN(_) => Nil
- case THROW() => Nil
+ case THROW(_) => Nil
case _ =>
if (closed) {
dump
@@ -422,40 +422,50 @@ trait BasicBlocks {
private def succString = if (successors.isEmpty) "[S: N/A]" else successors.distinct.mkString("[S: ", ", ", "]")
private def predString = if (predecessors.isEmpty) "[P: N/A]" else predecessors.distinct.mkString("[P: ", ", ", "]")
- def fullString: String = List("Block", label, succString, predString) mkString " "
override def toString(): String = "" + label
- def flagsString: String =
- ("block " + label + (
- if (hasFlag(LOOP_HEADER)) " <loopheader> "
- else if (hasFlag(IGNORING)) " <ignore> "
- else if (hasFlag(EX_HEADER)) " <exheader> "
- else if (hasFlag(CLOSED)) " <closed> "
- else if (hasFlag(DIRTYSUCCS)) " <dirtysuccs> "
- else if (hasFlag(DIRTYPREDS)) " <dirtypreds> "
- else ""
- ))
- }
+ def blockContents = {
+ def posStr(p: Position) = if (p.isDefined) p.line.toString else "<??>"
+ val xs = this.toList map (instr => posStr(instr.pos) + "\t" + instr)
+ xs.mkString(fullString + " {\n ", "\n ", "\n}")
+ }
+ def predContents = predecessors.map(_.blockContents).mkString(predecessors.size + " preds:\n", "\n", "\n")
+ def succContents = successors.map(_.blockContents).mkString(successors.size + " succs:\n", "\n", "\n")
+ def fullString: String = List("Block", label, succString, predString, flagsString) mkString " "
+ def flagsString: String = BBFlags.flagsToString(flags)
+ }
}
object BBFlags {
+ val flagMap = Map[Int, String](
+ LOOP_HEADER -> "loopheader",
+ IGNORING -> "ignore",
+ EX_HEADER -> "exheader",
+ CLOSED -> "closed",
+ DIRTYSUCCS -> "dirtysuccs",
+ DIRTYPREDS -> "dirtypreds"
+ )
+ def flagsToString(flags: Int) = {
+ flagMap collect { case (bit, name) if (bit & flags) != 0 => "<" + name + ">" } mkString " "
+ }
+
/** This block is a loop header (was translated from a while). */
- final val LOOP_HEADER = 0x00000001
+ final val LOOP_HEADER = (1 << 0)
/** Ignoring mode: emit instructions are dropped. */
- final val IGNORING = 0x00000002
+ final val IGNORING = (1 << 1)
/** This block is the header of an exception handler. */
- final val EX_HEADER = 0x00000004
+ final val EX_HEADER = (1 << 2)
/** This block is closed. No new instructions can be added. */
- final val CLOSED = 0x00000008
+ final val CLOSED = (1 << 3)
/** Code has been changed, recompute successors. */
- final val DIRTYSUCCS = 0x00000010
+ final val DIRTYSUCCS = (1 << 4)
/** Code has been changed, recompute predecessors. */
- final val DIRTYPREDS = 0x00000020
+ final val DIRTYPREDS = (1 << 5)
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala
index e5b94076e8..89c5ca18a9 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala
@@ -6,9 +6,10 @@
package scala.tools.nsc
package backend
-package icode;
+package icode
-import scala.collection._
+import scala.collection.{ mutable, immutable, generic }
+import util.{ Position, NoPosition }
/**
* Exception handlers are pieces of code that `handle' exceptions on
@@ -17,10 +18,14 @@ import scala.collection._
* all our handlers will catch `Throwable' and rely on proper ordering
* in the generated code to preserve nesting.
*/
-trait ExceptionHandlers { self: ICodes =>
- import global.{Symbol, NoSymbol};
+trait ExceptionHandlers {
+ self: ICodes =>
- class ExceptionHandler(val method: IMethod, val label: String, val cls: Symbol) {
+ import global.{ definitions, Symbol, NoSymbol }
+ import definitions.{ ThrowableClass }
+
+ class ExceptionHandler(val method: IMethod, val label: String, val cls: Symbol, val pos: Position) {
+ def loadExceptionClass = if (cls == NoSymbol) ThrowableClass else cls
private var _startBlock: BasicBlock = _;
var finalizer: Finalizer = _;
@@ -31,18 +36,18 @@ trait ExceptionHandlers { self: ICodes =>
_startBlock = b;
b.exceptionHandlerStart = true
}
- def startBlock = _startBlock;
+ def startBlock = _startBlock
/** The list of blocks that are covered by this exception handler */
var covered: immutable.Set[BasicBlock] = immutable.HashSet.empty[BasicBlock]
- def addCoveredBlock(b: BasicBlock): ExceptionHandler = {
+ def addCoveredBlock(b: BasicBlock): this.type = {
covered = covered + b
this
}
/** Is `b' covered by this exception handler? */
- def covers(b: BasicBlock): Boolean = covered(b);
+ def covers(b: BasicBlock): Boolean = covered(b)
/** The body of this exception handler. May contain 'dead' blocks (which will not
* make it into generated code because linearizers may not include them) */
@@ -54,23 +59,24 @@ trait ExceptionHandlers { self: ICodes =>
/** A standard copy constructor */
def this(other: ExceptionHandler) = {
- this(other.method, other.label, other.cls);
- covered = other.covered;
- setStartBlock(other.startBlock);
- finalizer = other.finalizer;
+ this(other.method, other.label, other.cls, other.pos)
+
+ covered = other.covered
+ setStartBlock(other.startBlock)
+ finalizer = other.finalizer
}
- def dup: ExceptionHandler = new ExceptionHandler(this);
+ def dup: ExceptionHandler = new ExceptionHandler(this)
}
- class Finalizer(method: IMethod, label: String) extends ExceptionHandler(method, label, NoSymbol) {
- override def toString() = "finalizer_" + label;
-
- override def dup: Finalizer = new Finalizer(method, label);
+ class Finalizer(method: IMethod, label: String, pos: Position) extends ExceptionHandler(method, label, NoSymbol, pos) {
+ override def toString() = "finalizer_" + label
+ override def dup: Finalizer = new Finalizer(method, label, pos)
}
- object NoFinalizer extends Finalizer(null, "<no finalizer>") {
- override def startBlock: BasicBlock = error("NoFinalizer cannot have a start block.");
+ object NoFinalizer extends Finalizer(null, "<no finalizer>", NoPosition) {
+ override def startBlock: BasicBlock = error("NoFinalizer cannot have a start block.");
override def setStartBlock(b: BasicBlock): Unit = error("NoFinalizer cannot have a start block.");
+ override def dup = this
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index a835ac5eb9..d68b678610 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -50,15 +50,6 @@ abstract class GenICode extends SubComponent {
var unit: CompilationUnit = _
- // We assume definitions are already initialized
- val STRING = REFERENCE(StringClass)
-
- // this depends on the backend! should be changed.
- val ANY_REF_CLASS = REFERENCE(ObjectClass)
- val SCALA_NOTHING = REFERENCE(NothingClass)
- val SCALA_NULL = REFERENCE(NullClass)
- val THROWABLE = REFERENCE(ThrowableClass)
-
override def run {
scalaPrimitives.init
classes.clear
@@ -184,6 +175,18 @@ abstract class GenICode extends SubComponent {
case _ =>
genLoad(tree, ctx, UNIT)
}
+
+ private def genThrow(expr: Tree, ctx: Context): (Context, TypeKind) = {
+ require(expr.tpe <:< ThrowableClass.tpe)
+
+ val thrownKind = toTypeKind(expr.tpe)
+ val ctx1 = genLoad(expr, ctx, thrownKind)
+ ctx1.bb.emit(THROW(expr.tpe.typeSymbol), expr.pos)
+ ctx1.bb.enterIgnoreMode
+
+ (ctx1, NothingReference)
+ }
+
/**
* Generate code for primitive arithmetic operations.
* Returns (Context, Generated Type)
@@ -310,7 +313,7 @@ abstract class GenICode extends SubComponent {
var ctx1 = genLoadQualifier(fun, ctx)
ctx1.bb.emit(Seq(
- DUP(ANY_REF_CLASS),
+ DUP(ObjectReference),
STORE_LOCAL(monitor),
MONITOR_ENTER() setPos tree.pos
))
@@ -333,7 +336,7 @@ abstract class GenICode extends SubComponent {
exhCtx.bb.emit(Seq(
LOAD_LOCAL(monitor),
MONITOR_EXIT() setPos tree.pos,
- THROW()
+ THROW(ThrowableClass)
))
exhCtx.bb.enterIgnoreMode
exhCtx
@@ -428,7 +431,7 @@ abstract class GenICode extends SubComponent {
if (scalaPrimitives.isArithmeticOp(code))
genArithmeticOp(tree, ctx, code)
else if (code == scalaPrimitives.CONCAT)
- (genStringConcat(tree, ctx), STRING)
+ (genStringConcat(tree, ctx), StringReference)
else if (code == scalaPrimitives.HASH)
(genScalaHash(receiver, ctx), INT)
else if (isArrayOp(code))
@@ -709,10 +712,8 @@ abstract class GenICode extends SubComponent {
case t @ Try(_, _, _) => genLoadTry(t, ctx, (x: TypeKind) => generatedType = x)
case Throw(expr) =>
- val ctx1 = genLoad(expr, ctx, THROWABLE)
- ctx1.bb.emit(THROW(), tree.pos)
- ctx1.bb.enterIgnoreMode
- generatedType = SCALA_NOTHING
+ val (ctx1, expectedType) = genThrow(expr, ctx)
+ generatedType = expectedType
ctx1
case New(tpt) =>
@@ -737,9 +738,9 @@ abstract class GenICode extends SubComponent {
ctx1.bb.emit(DROP(l), fun.pos)
if (cast) {
ctx1.bb.emit(Seq(
- NEW(REFERENCE(definitions.getClass("ClassCastException"))),
- DUP(ANY_REF_CLASS),
- THROW()
+ NEW(REFERENCE(definitions.ClassCastExceptionClass)),
+ DUP(ObjectReference),
+ THROW(definitions.ClassCastExceptionClass)
))
} else
ctx1.bb.emit(CONSTANT(Constant(false)))
@@ -875,22 +876,19 @@ abstract class GenICode extends SubComponent {
val sym = fun.symbol
if (sym.isLabel) { // jump to a label
- val label = ctx.labels.get(sym) match {
- case Some(l) => l
-
+ val label = ctx.labels.getOrElse(sym, {
// it is a forward jump, scan for labels
- case None =>
- log("Performing scan for label because of forward jump.")
- scanForLabels(ctx.defdef, ctx)
- ctx.labels.get(sym) match {
- case Some(l) =>
- log("Found label: " + l)
- l
- case _ =>
- abort("Unknown label target: " + sym +
- " at: " + (fun.pos) + ": ctx: " + ctx)
- }
- }
+ log("Performing scan for label because of forward jump.")
+ scanForLabels(ctx.defdef, ctx)
+ ctx.labels.get(sym) match {
+ case Some(l) =>
+ log("Found label: " + l)
+ l
+ case _ =>
+ abort("Unknown label target: " + sym +
+ " at: " + (fun.pos) + ": ctx: " + ctx)
+ }
+ })
val ctx1 = genLoadLabelArguments(args, label, ctx)
ctx1.bb.emitOnly(if (label.anchored) JUMP(label.block) else PJUMP(label))
ctx1.bb.enterIgnoreMode
@@ -950,7 +948,7 @@ abstract class GenICode extends SubComponent {
case ApplyDynamic(qual, args) =>
assert(!forMSIL)
ctx.clazz.bootstrapClass = Some("scala.runtime.DynamicDispatch")
- val ctx1 = genLoad(qual, ctx, ANY_REF_CLASS)
+ val ctx1 = genLoad(qual, ctx, ObjectReference)
genLoadArguments(args, tree.symbol.info.paramTypes, ctx1)
ctx1.bb.emit(CALL_METHOD(tree.symbol, InvokeDynamic), tree.pos)
ctx1
@@ -1036,7 +1034,7 @@ abstract class GenICode extends SubComponent {
generatedType = DOUBLE
case (NullTag, _) =>
ctx.bb.emit(CONSTANT(value), tree.pos);
- generatedType = SCALA_NULL
+ generatedType = NullReference
case _ =>
ctx.bb.emit(CONSTANT(value), tree.pos);
generatedType = toTypeKind(tree.tpe)
@@ -1129,7 +1127,7 @@ abstract class GenICode extends SubComponent {
}
private def adapt(from: TypeKind, to: TypeKind, ctx: Context, pos: Position): Unit = {
- if (!(from <:< to) && !(from == SCALA_NULL && to == SCALA_NOTHING)) {
+ if (!(from <:< to) && !(from == NullReference && to == NothingReference)) {
to match {
case UNIT =>
ctx.bb.emit(DROP(from), pos)
@@ -1142,14 +1140,14 @@ abstract class GenICode extends SubComponent {
assert(!from.isReferenceType && !to.isReferenceType, "type error: can't convert from " + from + " to " + to +" in unit "+this.unit)
ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), pos);
}
- } else if (from == SCALA_NOTHING) {
- ctx.bb.emit(THROW())
+ } else if (from == NothingReference) {
+ ctx.bb.emit(THROW(ThrowableClass))
ctx.bb.enterIgnoreMode
- } else if (from == SCALA_NULL) {
+ } else if (from == NullReference) {
ctx.bb.emit(DROP(from))
ctx.bb.emit(CONSTANT(Constant(null)))
}
- else if (from == THROWABLE) {
+ else if (from == ThrowableReference) {
log("Inserted check-cast on throwable to " + to + " at " + pos)
ctx.bb.emit(CHECK_CAST(to))
}
@@ -1351,7 +1349,7 @@ abstract class GenICode extends SubComponent {
getMember(ScalaRunTimeModule, "hash")
}
- val ctx1 = genLoad(tree, ctx, ANY_REF_CLASS)
+ val ctx1 = genLoad(tree, ctx, ObjectReference)
ctx1.bb.emit(CALL_METHOD(hashMethod, Static(false)))
ctx1
}
@@ -1429,9 +1427,9 @@ abstract class GenICode extends SubComponent {
// special-case reference (in)equality test for null (null eq x, x eq null)
lazy val nonNullSide = ifOneIsNull(l, r)
if (isReferenceEqualityOp(code) && nonNullSide != null) {
- val ctx1 = genLoad(nonNullSide, ctx, ANY_REF_CLASS)
+ val ctx1 = genLoad(nonNullSide, ctx, ObjectReference)
ctx1.bb.emitOnly(
- CZJUMP(thenCtx.bb, elseCtx.bb, op, ANY_REF_CLASS)
+ CZJUMP(thenCtx.bb, elseCtx.bb, op, ObjectReference)
)
}
else {
@@ -1557,8 +1555,8 @@ abstract class GenICode extends SubComponent {
getMember(ScalaRunTimeModule, nme.inlinedEquals)
}
- val ctx1 = genLoad(l, ctx, ANY_REF_CLASS)
- val ctx2 = genLoad(r, ctx1, ANY_REF_CLASS)
+ val ctx1 = genLoad(l, ctx, ObjectReference)
+ val ctx2 = genLoad(r, ctx1, ObjectReference)
ctx2.bb.emit(CALL_METHOD(equalsMethod, if (settings.XO.value) Dynamic else Static(false)))
ctx2.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL))
ctx2.bb.close
@@ -1566,30 +1564,30 @@ abstract class GenICode extends SubComponent {
else {
if (isNull(l))
// null == expr -> expr eq null
- genLoad(r, ctx, ANY_REF_CLASS).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS)
+ genLoad(r, ctx, ObjectReference).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference)
else if (isNull(r)) {
// expr == null -> expr eq null
- genLoad(l, ctx, ANY_REF_CLASS).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS)
+ genLoad(l, ctx, ObjectReference).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference)
} else {
val eqEqTempLocal = getTempLocal
- var ctx1 = genLoad(l, ctx, ANY_REF_CLASS)
+ var ctx1 = genLoad(l, ctx, ObjectReference)
// dicey refactor section
lazy val nonNullCtx = ctx1.newBlock
// l == r -> if (l eq null) r eq null else l.equals(r)
- ctx1 = genLoad(r, ctx1, ANY_REF_CLASS)
+ ctx1 = genLoad(r, ctx1, ObjectReference)
val nullCtx = ctx1.newBlock
ctx1.bb.emitOnly(
STORE_LOCAL(eqEqTempLocal) setPos l.pos,
- DUP(ANY_REF_CLASS),
- CZJUMP(nullCtx.bb, nonNullCtx.bb, EQ, ANY_REF_CLASS)
+ DUP(ObjectReference),
+ CZJUMP(nullCtx.bb, nonNullCtx.bb, EQ, ObjectReference)
)
nullCtx.bb.emitOnly(
- DROP(ANY_REF_CLASS) setPos l.pos, // type of AnyRef
+ DROP(ObjectReference) setPos l.pos, // type of AnyRef
LOAD_LOCAL(eqEqTempLocal),
- CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS)
+ CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference)
)
nonNullCtx.bb.emitOnly(
LOAD_LOCAL(eqEqTempLocal) setPos l.pos,
@@ -1873,13 +1871,9 @@ abstract class GenICode extends SubComponent {
buf.toString()
}
- /** PP: This instruction was only emitted when settings.Xdce.value = true,
- * but I don't understand the condition. It seems like the exception handler
- * stacks won't balance without it, so put it in under -Ycheck too.
- */
- def maybeLoadException(ctx: Context) = {
- if (settings.Xdce.value || !settings.check.isDefault)
- ctx.bb.emit(LOAD_EXCEPTION())
+ def loadException(ctx: Context, exh: ExceptionHandler, pos: Position) = {
+ log("Emitting LOAD_EXCEPTION for class: " + exh.loadExceptionClass)
+ ctx.bb.emit(LOAD_EXCEPTION(exh.loadExceptionClass) setPos pos, pos)
}
def this(other: Context) = {
@@ -1983,9 +1977,9 @@ abstract class GenICode extends SubComponent {
* 'covered' by this exception handler (in addition to the
* previously active handlers).
*/
- def newHandler(cls: Symbol, resultKind: TypeKind): ExceptionHandler = {
+ def newHandler(cls: Symbol, resultKind: TypeKind, pos: Position): ExceptionHandler = {
handlerCount += 1
- val exh = new ExceptionHandler(method, "" + handlerCount, cls)
+ val exh = new ExceptionHandler(method, "" + handlerCount, cls, pos)
exh.resultKind = resultKind
method.addHandler(exh)
handlers = exh :: handlers
@@ -2093,15 +2087,15 @@ abstract class GenICode extends SubComponent {
val finalizerExh = if (finalizer != EmptyTree) Some({
- val exh = outerCtx.newHandler(NoSymbol, toTypeKind(finalizer.tpe)) // finalizer covers exception handlers
+ val exh = outerCtx.newHandler(NoSymbol, toTypeKind(finalizer.tpe), finalizer.pos) // finalizer covers exception handlers
this.addActiveHandler(exh) // .. and body aswell
val ctx = finalizerCtx.enterHandler(exh)
val exception = ctx.makeLocal(finalizer.pos, ThrowableClass.tpe, "exc")
- maybeLoadException(ctx)
+ loadException(ctx, exh, finalizer.pos)
ctx.bb.emit(STORE_LOCAL(exception));
val ctx1 = genLoad(finalizer, ctx, UNIT);
ctx1.bb.emit(LOAD_LOCAL(exception));
- ctx1.bb.emit(THROW());
+ ctx1.bb.emit(THROW(ThrowableClass));
ctx1.bb.enterIgnoreMode;
ctx1.bb.close
finalizerCtx.endHandler()
@@ -2109,9 +2103,9 @@ abstract class GenICode extends SubComponent {
}) else None
val exhs = handlers.map { handler =>
- val exh = this.newHandler(handler._1, handler._2)
+ val exh = this.newHandler(handler._1, handler._2, tree.pos)
var ctx1 = outerCtx.enterHandler(exh)
- maybeLoadException(ctx1)
+ loadException(ctx1, exh, tree.pos)
ctx1 = handler._3(ctx1)
// emit finalizer
val ctx2 = emitFinalizer(ctx1)
@@ -2162,10 +2156,10 @@ abstract class GenICode extends SubComponent {
if (finalizer != EmptyTree) {
// finalizer is covers try and all catch blocks, i.e.
// try { try { .. } catch { ..} } finally { .. }
- val exh = outerCtx.newHandler(NoSymbol, UNIT)
+ val exh = outerCtx.newHandler(NoSymbol, UNIT, tree.pos)
this.addActiveHandler(exh)
val ctx = finalizerCtx.enterHandler(exh)
- maybeLoadException(ctx)
+ loadException(ctx, exh, tree.pos)
val ctx1 = genLoad(finalizer, ctx, UNIT)
// need jump for the ICode to be valid. MSIL backend will emit `Endfinally` instead.
ctx1.bb.closeWith(JUMP(afterCtx.bb))
@@ -2173,9 +2167,9 @@ abstract class GenICode extends SubComponent {
}
for (handler <- handlers) {
- val exh = this.newHandler(handler._1, handler._2)
+ val exh = this.newHandler(handler._1, handler._2, tree.pos)
var ctx1 = outerCtx.enterHandler(exh)
- maybeLoadException(ctx1)
+ loadException(ctx1, exh, tree.pos)
ctx1 = handler._3(ctx1)
// msil backend will emit `Leave` to jump out of a handler
ctx1.bb.closeWith(JUMP(afterCtx.bb))
diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala
index 78862a90cb..8e92beebc3 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala
@@ -59,15 +59,24 @@ abstract class ICodeCheckers {
var method: IMethod = _
var code: Code = _
- val in: Map[BasicBlock, TypeStack] = new HashMap()
+ val in: Map[BasicBlock, TypeStack] = new HashMap()
val out: Map[BasicBlock, TypeStack] = new HashMap()
+ val emptyStack = new TypeStack() {
+ override def toString = "<empty>"
+ }
- val emptyStack = new TypeStack()
-
- val STRING = REFERENCE(definitions.StringClass)
- val BOXED_UNIT = REFERENCE(definitions.BoxedUnitClass)
- val SCALA_NOTHING = REFERENCE(definitions.NothingClass)
- val SCALA_NULL = REFERENCE(definitions.NullClass)
+ /** The presence of emptyStack means that path has not yet been checked
+ * (and may not be empty).
+ */
+ def notChecked(ts: TypeStack) = ts eq emptyStack
+ def initMaps(bs: Seq[BasicBlock]): Unit = {
+ in.clear()
+ out.clear()
+ bs foreach { b =>
+ in(b) = emptyStack
+ out(b) = emptyStack
+ }
+ }
/** A wrapper to route log messages to debug output also.
*/
@@ -83,17 +92,13 @@ abstract class ICodeCheckers {
}
private def posStr(p: Position) =
- if (p.isDefined) p.line.toString else "<no pos>"
+ if (p.isDefined) p.line.toString else "<??>"
private def indent(s: String, spaces: Int): String = indent(s, " " * spaces)
private def indent(s: String, prefix: String): String = {
val lines = s split "\\n"
lines map (prefix + _) mkString "\n"
}
- private def blockAsString(bl: BasicBlock) = {
- val s = bl.toList map (instr => posStr(instr.pos) + "\t" + instr) mkString (bl.fullString + " {\n ", "\n ", "\n}")
- indent(s, "// ")
- }
/** Only called when m1 < m2, so already known that (m1 ne m2).
*/
@@ -103,7 +108,7 @@ abstract class ICodeCheckers {
)
def check(cls: IClass) {
- logChecker("\n** Checking class " + cls)
+ logChecker("\n<<-- Checking class " + cls + " -->>")
clasz = cls
for (f1 <- cls.fields ; f2 <- cls.fields ; if f1 < f2)
@@ -118,36 +123,27 @@ abstract class ICodeCheckers {
}
def check(m: IMethod) {
- logChecker("\n** Checking method " + m)
+ logChecker("\n<< Checking method " + m.symbol.name + " >>")
method = m
if (!m.isAbstractMethod)
check(m.code)
}
def check(c: Code) {
- var worklist: Buffer[BasicBlock] = new ListBuffer()
+ val worklist: Buffer[BasicBlock] = new ListBuffer()
+ def append(elems: List[BasicBlock]) =
+ worklist ++= (elems filterNot (worklist contains _))
- def append(elems: List[BasicBlock]) = elems foreach appendBlock;
- def appendBlock(bl: BasicBlock) =
- if (!(worklist contains bl))
- worklist += bl
-
- in.clear;
- out.clear;
- code = c;
+ code = c
worklist += c.startBlock
- for (bl <- c.blocks) {
- in += (bl -> emptyStack)
- out += (bl -> emptyStack)
- }
+ initMaps(c.blocks)
while (worklist.nonEmpty) {
- val block = worklist(0);
- worklist.trimStart(1);
- val output = check(block, in(block));
- if (output != out(block) || (out(block) eq emptyStack)) {
- if (block.successors.nonEmpty || block.successors.nonEmpty)
- logChecker("Output changed for " + block.fullString)
+ val block = worklist remove 0
+ val output = check(block, in(block))
+ if (output != out(block) || notChecked(out(block))) {
+ if (block.successors.nonEmpty)
+ logChecker("** Output change for %s: %s -> %s".format(block, out(block), output))
out(block) = output
append(block.successors)
@@ -164,14 +160,21 @@ abstract class ICodeCheckers {
def meet(bl: BasicBlock) {
val preds = bl.predecessors
+ def hasNothingType(s: TypeStack) = s.nonEmpty && (s.head == NothingReference)
+ def hasNullType(s: TypeStack) = s.nonEmpty && (s.head == NullReference)
+
/** XXX workaround #1: one stack empty, the other has BoxedUnit.
* One example where this arises is:
*
* def f(b: Boolean): Unit = synchronized { if (b) () }
*/
- def isAllUnits(s1: TypeStack, s2: TypeStack) = {
- List(s1, s2) forall (x => x.types forall (_ == BOXED_UNIT))
+ def allUnits(s: TypeStack) = s.types forall (_ == BoxedUnitReference)
+
+ def ifAthenB[T](f: T => Boolean): PartialFunction[(T, T), T] = {
+ case (x1, x2) if f(x1) => x2
+ case (x1, x2) if f(x2) => x1
}
+
/** XXX workaround #2: different stacks heading into an exception
* handler which will clear them anyway. Examples where it arises:
*
@@ -179,9 +182,6 @@ abstract class ICodeCheckers {
*/
def isHandlerBlock() = bl.exceptionHandlerStart
- /** The presence of emptyStack means that path has not yet been checked
- * (and may not be empty) thus the reference eq tests.
- */
def meet2(s1: TypeStack, s2: TypeStack): TypeStack = {
def workaround(msg: String) = {
checkerDebug(msg + ": " + method + " at block " + bl)
@@ -190,27 +190,34 @@ abstract class ICodeCheckers {
new TypeStack()
}
def incompatibleString = (
- "Incompatible stacks: " + s1 + " and " + s2 + " in " + method + " at entry to:\n" +
- blockAsString(bl)
+ "Incompatible stacks: " + s1 + " and " + s2 + " in " + method + " at entry to block " + bl.label + ":\n" +
+ indent(bl.predContents, "// ") +
+ indent(bl.succContents, "// ") +
+ indent(bl.blockContents, "// ")
)
- if (s1 eq emptyStack) s2
- else if (s2 eq emptyStack) s1
- else if (s1.length != s2.length) {
- if (isAllUnits(s1, s2))
- workaround("Ignoring mismatched boxed units")
- else if (isHandlerBlock)
- workaround("Ignoring mismatched stacks entering exception handler")
- else
- throw new CheckerException(incompatibleString)
+ val f: ((TypeStack, TypeStack)) => TypeStack = {
+ ifAthenB(notChecked) orElse ifAthenB(hasNothingType) orElse {
+ case (s1: TypeStack, s2: TypeStack) =>
+ if (s1.length != s2.length) {
+ if (allUnits(s1) && allUnits(s2))
+ workaround("Ignoring mismatched boxed units")
+ else if (isHandlerBlock)
+ workaround("Ignoring mismatched stacks entering exception handler")
+ else
+ throw new CheckerException(incompatibleString)
+ }
+ else {
+ val newStack = new TypeStack((s1.types, s2.types).zipped map lub)
+ if (newStack.isEmpty || s1.types == s2.types) () // not interesting to report
+ else checkerDebug("Checker created new stack:\n (%s, %s) => %s".format(s1, s2, newStack))
+
+ newStack
+ }
+ }
}
- else {
- val newStack = new TypeStack((s1.types, s2.types).zipped map lub)
- if (newStack.nonEmpty)
- checkerDebug("Checker created new stack:\n (%s, %s) => %s".format(s1, s2, newStack))
- newStack
- }
+ f((s1, s2))
}
if (preds.nonEmpty) {
@@ -221,12 +228,52 @@ abstract class ICodeCheckers {
private var instruction: Instruction = null
private var basicBlock: BasicBlock = null
+ private var stringConcatDepth = 0
+ private def stringConcatIndent() = " " * stringConcatDepth
+ private def currentInstrString: String = {
+ val (indent, str) = this.instruction match {
+ case CALL_PRIMITIVE(StartConcat) =>
+ val x = stringConcatIndent()
+ stringConcatDepth += 1
+ (x, "concat(")
+ case CALL_PRIMITIVE(EndConcat) =>
+ if (stringConcatDepth > 0) {
+ stringConcatDepth -= 1
+ (stringConcatIndent(), ") // end concat")
+ }
+ else ("", "")
+ case _ =>
+ (stringConcatIndent(), this.instruction match {
+ case CALL_PRIMITIVE(StringConcat(el)) => "..."
+ case null => "null"
+ case cm @ CALL_METHOD(_, _) => if (clasz.symbol == cm.hostClass) cm.toShortString else cm.toString
+ case x => x.toString
+ })
+ }
+ indent + str
+ }
+ /** A couple closure creators to reduce noise in the output: when multiple
+ * items are pushed or popped, this lets us print something short and sensible
+ * for those beyond the first.
+ */
+ def mkInstrPrinter(f: Int => String): () => String = {
+ var counter = -1
+ val indent = stringConcatIndent()
+ () => {
+ counter += 1
+ if (counter == 0) currentInstrString
+ else indent + f(counter)
+ }
+ }
+ def defaultInstrPrinter: () => String = mkInstrPrinter(_ => "\"\"\"")
/**
* Check the basic block to be type correct and return the
* produced type stack.
*/
def check(b: BasicBlock, initial: TypeStack): TypeStack = {
+ this.basicBlock = b
+
logChecker({
val prefix = "** Checking " + b.fullString
@@ -246,70 +293,97 @@ abstract class ICodeCheckers {
sp + stack.length + arrow
}
+ def printStackString(isPush: Boolean, value: TypeKind, instrString: String) = {
+ val pushString = if (isPush) "+" else "-"
+ val posString = posStr(this.instruction.pos)
+
+ checkerDebug("%-70s %-4s %s %s".format(sizeString(isPush) + value, posString, pushString, instrString))
+ }
def _popStack: TypeKind = {
if (stack.isEmpty) {
error("Popped empty stack in " + b.fullString + ", throwing a Unit")
return UNIT
}
-
- val res = stack.pop
- checkerDebug(sizeString(false) + res)
- res
+ stack.pop
}
- def popStack = { checkStack(1) ; _popStack }
- def popStack2 = { checkStack(2) ; (_popStack, _popStack) }
- def popStack3 = { checkStack(3) ; (_popStack, _popStack, _popStack) }
- def clearStack() = 1 to stack.length foreach (_ => popStack)
-
- def pushStack(xs: TypeKind*): Unit = {
+ def popStackN(num: Int, instrFn: () => String = defaultInstrPrinter) = {
+ List.range(0, num) map { _ =>
+ val res = _popStack
+ printStackString(false, res, instrFn())
+ res
+ }
+ }
+ def pushStackN(xs: Seq[TypeKind], instrFn: () => String) = {
xs foreach { x =>
- if (x == UNIT) {
- /** PP: I admit I'm not yet figuring out how the stacks will balance when
- * we ignore UNIT, but I expect this knowledge will emerge naturally.
- * In the meantime I'm logging it to help me out.
- */
- logChecker("Ignoring pushed UNIT")
- }
- else {
- stack push x
- checkerDebug(sizeString(true) + x)
- }
+ stack push x
+ printStackString(true, x, instrFn())
}
}
- this.basicBlock = b
+ def popStack = { checkStack(1) ; popStackN(1) match { case List(x) => x } }
+ def popStack2 = { checkStack(2) ; popStackN(2) match { case List(x, y) => (x, y) } }
+ def popStack3 = { checkStack(3) ; popStackN(3) match { case List(x, y, z) => (x, y, z) } }
+
+ /** Called by faux instruction LOAD_EXCEPTION to wipe out the stack. */
+ def clearStack() = {
+ if (stack.nonEmpty)
+ logChecker("Wiping out the " + stack.length + " element stack for exception handler: " + stack)
+
+ 1 to stack.length foreach (_ => popStack)
+ }
+
+ def pushStack(xs: TypeKind*): Unit = {
+ pushStackN(xs filterNot (_ == UNIT), defaultInstrPrinter)
+ }
def typeError(k1: TypeKind, k2: TypeKind) {
error("\n expected: " + k1 + "\n found: " + k2)
}
+ def isSubtype(k1: TypeKind, k2: TypeKind) = (k1 <:< k2) || {
+ import platform.isMaybeBoxed
+
+ (k1, k2) match {
+ case (REFERENCE(_), REFERENCE(_)) if k1.isInterfaceType || k2.isInterfaceType =>
+ logChecker("Considering %s <:< %s because at least one is an interface".format(k1, k2))
+ true
+ case (REFERENCE(cls1), REFERENCE(cls2)) if isMaybeBoxed(cls1) || isMaybeBoxed(cls2) =>
+ logChecker("Considering %s <:< %s because at least one might be a boxed primitive".format(cls1, cls2))
+ true
+ case _ =>
+ false
+ }
+ }
+
+ /** Return true if k1 is a subtype of any of the following types,
+ * according to the somewhat relaxed subtyping standards in effect here.
+ */
+ def isOneOf(k1: TypeKind, kinds: TypeKind*) = kinds exists (k => isSubtype(k1, k))
def subtypeTest(k1: TypeKind, k2: TypeKind): Unit =
- if (k1 <:< k2) ()
+ if (isSubtype(k1, k2)) ()
else typeError(k2, k1)
for (instr <- b) {
+ this.instruction = instr
def checkLocal(local: Local): Unit = {
- (method lookupLocal local.sym.name) getOrElse {
+ method lookupLocal local.sym.name getOrElse {
error(" " + local + " is not defined in method " + method)
}
}
-
- def checkField(obj: TypeKind, field: Symbol) {
- obj match {
- case REFERENCE(sym) =>
- if (sym.info.member(field.name) == NoSymbol)
- error(" " + field + " is not defined in class " + clasz);
- case _ =>
- error(" expected reference type, but " + obj + " found");
- }
+ def checkField(obj: TypeKind, field: Symbol): Unit = obj match {
+ case REFERENCE(sym) =>
+ if (sym.info.member(field.name) == NoSymbol)
+ error(" " + field + " is not defined in class " + clasz);
+ case _ =>
+ error(" expected reference type, but " + obj + " found");
}
/** Checks that tpe is a subtype of one of the allowed types */
- def checkType(tpe: TypeKind, allowed: TypeKind*) {
- if (isOneOf(tpe, allowed: _*)) ()
+ def checkType(tpe: TypeKind, allowed: TypeKind*) = (
+ if (allowed exists (k => isSubtype(tpe, k))) ()
else error(tpe + " is not one of: " + allowed.mkString("{ ", ", ", " }"))
- }
+ )
def checkNumeric(tpe: TypeKind) =
checkType(tpe, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE)
@@ -326,7 +400,10 @@ abstract class ICodeCheckers {
def checkMethodArgs(method: Symbol) {
val params = method.info.paramTypes
checkStack(params.length)
- params.reverse foreach (tpe => checkType(popStack, toTypeKind(tpe)))
+ (
+ popStackN(params.length, mkInstrPrinter(num => "<arg" + num + ">")),
+ params.reverse map toTypeKind).zipped foreach ((x, y) => checkType(x, y)
+ )
}
/** Checks that the object passed as receiver has a method
@@ -344,10 +421,16 @@ abstract class ICodeCheckers {
checkBool(method.owner == clasz.symbol,
"Cannot call private method of " + method.owner.fullName
+ " from " + clasz.symbol.fullName);
- else if (method.isProtected)
- checkBool(clasz.symbol isSubClass method.owner,
+ else if (method.isProtected) {
+ val isProtectedOK = (
+ (clasz.symbol isSubClass method.owner) ||
+ (clasz.symbol.typeOfThis.typeSymbol isSubClass method.owner) // see pos/bug780.scala
+ )
+
+ checkBool(isProtectedOK,
"Cannot call protected method of " + method.owner.fullName
+ " from " + clasz.symbol.fullName);
+ }
case ARRAY(_) =>
checkBool(receiver.toType.member(method.name) != NoSymbol,
@@ -360,8 +443,6 @@ abstract class ICodeCheckers {
def checkBool(cond: Boolean, msg: String) =
if (!cond) error(msg)
- this.instruction = instr
-
if (settings.debug.value) {
log("PC: " + instr)
log("stack: " + stack)
@@ -420,10 +501,8 @@ abstract class ICodeCheckers {
case STORE_LOCAL(local) =>
checkLocal(local)
val actualType = popStack
- // PP: ThrowableReference is temporary to deal with exceptions
- // not yet appearing typed.
- if (actualType == ThrowableReference || local.kind == SCALA_NULL) ()
- else subtypeTest(actualType, local.kind)
+ if (local.kind != NullReference)
+ subtypeTest(actualType, local.kind)
case STORE_FIELD(field, true) => // static
val fieldType = toTypeKind(field.tpe)
@@ -434,7 +513,7 @@ abstract class ICodeCheckers {
val (value, obj) = popStack2
checkField(obj, field)
val fieldType = toTypeKind(field.tpe)
- if (fieldType == SCALA_NULL) ()
+ if (fieldType == NullReference) ()
else subtypeTest(value, fieldType)
case CALL_PRIMITIVE(primitive) =>
@@ -494,7 +573,7 @@ abstract class ICodeCheckers {
case EndConcat =>
checkType(popStack, ConcatClass)
- pushStack(STRING)
+ pushStack(StringReference)
case StringConcat(el) =>
checkType(popStack, el)
@@ -503,38 +582,24 @@ abstract class ICodeCheckers {
}
case CALL_METHOD(method, style) =>
- def paramCount = method.info.paramTypes.length
- def pushReturnType = pushStack(toTypeKind(method.info.resultType))
-
- style match {
- case Dynamic | InvokeDynamic =>
- checkStack(1 + paramCount)
- checkMethodArgs(method)
- checkMethod(popStack, method)
- pushReturnType
-
- case Static(onInstance) =>
- if (onInstance) {
- checkStack(1 + paramCount)
- checkBool(method.isPrivate || method.isConstructor,
- "Static call to non-private method.")
- checkMethodArgs(method)
- checkMethod(popStack, method)
- if (!method.isConstructor)
- pushReturnType
- }
- else {
- checkStack(paramCount);
- checkMethodArgs(method);
- pushReturnType
- }
-
- case SuperCall(mix) =>
- checkStack(1 + paramCount)
- checkMethodArgs(method)
- checkMethod(popStack, method)
- pushReturnType
+ // PP to ID: I moved the if (!method.isConstructor) check to cover all
+ // the styles to address checker failure. Can you confirm if the change
+ // was correct? If I remember right it's a matter of whether some brand
+ // of supercall should leave a value on the stack, and I know there is some
+ // trickery performed elsewhere regarding this.
+ val paramCount = method.info.paramTypes.length match {
+ case x if style.hasInstance => x + 1
+ case x => x
}
+ if (style == Static(true))
+ checkBool(method.isPrivate || method.isConstructor, "Static call to non-private method.")
+
+ checkStack(paramCount)
+ checkMethodArgs(method)
+ if (style.hasInstance)
+ checkMethod(popStack, method)
+ if (!method.isConstructor)
+ pushStack(toTypeKind(method.info.resultType))
case NEW(kind) =>
pushStack(kind)
@@ -587,11 +652,9 @@ abstract class ICodeCheckers {
if (kind.isValueType) checkType(top, kind)
else checkBool(!top.isValueType, "" + kind + " is a reference type, but " + top + " is not");
- case THROW() =>
- val thrown = popStack
- checkBool(thrown.toType <:< definitions.ThrowableClass.tpe,
- "Element on top of stack should implement 'Throwable': " + thrown);
- pushStack(SCALA_NOTHING)
+ case THROW(clasz) =>
+ checkType(popStack, toTypeKind(clasz.tpe))
+ pushStack(NothingReference)
case DROP(kind) =>
checkType(popStack, kind)
@@ -610,15 +673,15 @@ abstract class ICodeCheckers {
case BOX(kind) =>
checkType(popStack, kind)
- pushStack(icodes.ObjectReference)
+ pushStack(REFERENCE(definitions.boxedClass(kind.toType.typeSymbol)))
case UNBOX(kind) =>
popStack
pushStack(kind)
- case LOAD_EXCEPTION() =>
+ case LOAD_EXCEPTION(clasz) =>
clearStack()
- pushStack(ThrowableReference)
+ pushStack(REFERENCE(clasz))
case SCOPE_ENTER(_) | SCOPE_EXIT(_) =>
()
@@ -633,18 +696,15 @@ abstract class ICodeCheckers {
//////////////// Error reporting /////////////////////////
def error(msg: String) {
- ICodeCheckers.this.global.error("!! ICode checker fatality in " + method + " at:" + blockAsString(basicBlock) + ":\n " + msg)
+ ICodeCheckers.this.global.error(
+ "!! ICode checker fatality in " + method +
+ "\n at: " + basicBlock.fullString +
+ "\n error message: " + msg
+ )
}
def error(msg: String, stack: TypeStack) {
error(msg + "\n type stack: " + stack)
}
-
- //////////////////// Checking /////////////////////////////
-
- /** Return true if <code>k1</code> is a subtype of any of the following
- * types.
- */
- def isOneOf(k1: TypeKind, kinds: TypeKind*) = kinds exists (k1 <:< _)
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala
index 65b558b230..dc31edc569 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala
@@ -14,9 +14,9 @@ package icode
import java.io.PrintWriter
-import scala.collection.mutable.HashMap
+import scala.collection.{ mutable, immutable, generic }
import scala.tools.nsc.symtab._
-import analysis.{Liveness, ReachingDefinitions}
+import analysis.{ Liveness, ReachingDefinitions }
import scala.tools.nsc.symtab.classfile.ICodeReader
/** Glue together ICode parts.
@@ -36,26 +36,23 @@ abstract class ICodes extends AnyRef
with Repository
{
val global: Global
+ import global.{ definitions, settings }
/** The ICode representation of classes */
- var classes: HashMap[global.Symbol, IClass] = new HashMap()
+ val classes = new mutable.HashMap[global.Symbol, IClass]
/** Debugging flag */
- var isCheckerDebug: Boolean = global.settings.checkDebug.value
- def checkerDebug(msg: String) = if (isCheckerDebug) println(msg)
+ def shouldCheckIcode = settings.check contains global.genicode.phaseName
+ def checkerDebug(msg: String) = if (shouldCheckIcode && global.opt.debug) println(msg)
/** The ICode linearizer. */
- val linearizer: Linearizer =
- if (global.settings.Xlinearizer.value == "rpo")
- new ReversePostOrderLinearizer()
- else if (global.settings.Xlinearizer.value == "dfs")
- new DepthFirstLinerizer()
- else if (global.settings.Xlinearizer.value == "normal")
- new NormalLinearizer();
- else if (global.settings.Xlinearizer.value == "dump")
- new DumpLinearizer()
- else
- global.abort("Unknown linearizer: " + global.settings.Xlinearizer.value)
+ val linearizer: Linearizer = settings.Xlinearizer.value match {
+ case "rpo" => new ReversePostOrderLinearizer()
+ case "dfs" => new DepthFirstLinerizer()
+ case "normal" => new NormalLinearizer()
+ case "dump" => new DumpLinearizer()
+ case x => global.abort("Unknown linearizer: " + x)
+ }
/** Have to be careful because dump calls around, possibly
* re-entering methods which initiated the dump (like foreach
@@ -79,7 +76,7 @@ abstract class ICodes extends AnyRef
for (b <- m.code.blocks)
if (!b.closed) {
m.dump
- global.abort("Open block: " + b.flagsString)
+ global.abort("Open block: " + b + " " + b.flagsString)
}
}
@@ -91,9 +88,13 @@ abstract class ICodes extends AnyRef
val global: ICodes.this.global.type = ICodes.this.global
}
- lazy val ObjectReference: TypeKind = REFERENCE(global.definitions.ObjectClass)
- lazy val ThrowableReference: TypeKind = REFERENCE(global.definitions.ThrowableClass)
- lazy val AnyRefReference: TypeKind = REFERENCE(global.definitions.AnyRefClass)
+ lazy val AnyRefReference: TypeKind = REFERENCE(definitions.AnyRefClass)
+ lazy val BoxedUnitReference: TypeKind = REFERENCE(definitions.BoxedUnitClass)
+ lazy val NothingReference: TypeKind = REFERENCE(definitions.NothingClass)
+ lazy val NullReference: TypeKind = REFERENCE(definitions.NullClass)
+ lazy val ObjectReference: TypeKind = REFERENCE(definitions.ObjectClass)
+ lazy val StringReference: TypeKind = REFERENCE(definitions.StringClass)
+ lazy val ThrowableReference: TypeKind = REFERENCE(definitions.ThrowableClass)
object icodeReader extends ICodeReader {
lazy val global: ICodes.this.global.type = ICodes.this.global
@@ -102,10 +103,8 @@ abstract class ICodes extends AnyRef
/** A phase which works on icode. */
abstract class ICodePhase(prev: Phase) extends global.GlobalPhase(prev) {
override def erasedTypes = true
-
- override def apply(unit: global.CompilationUnit) {
- unit.icode foreach { c => apply(c) }
- }
+ override def apply(unit: global.CompilationUnit): Unit =
+ unit.icode foreach apply
def apply(cls: global.icodes.IClass): Unit
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala
index 701e5d3815..9bb704f8b7 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala
@@ -73,7 +73,7 @@ trait Linearizers { self: ICodes =>
case SWITCH(_, labels) =>
add(labels);
case RETURN(_) => ();
- case THROW() => ();
+ case THROW(clasz) => ();
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
index 6c3ec7fc19..5ee4245e35 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
@@ -36,7 +36,7 @@ import scala.tools.nsc.util.{Position,NoPosition}
case CJUMP(success, failure, cond, kind) =>
case CZJUMP(success, failure, cond, kind) =>
case RETURN(kind) =>
- case THROW() =>
+ case THROW(clasz) =>
case DROP(kind) =>
case DUP(kind) =>
case MONITOR_ENTER() =>
@@ -45,7 +45,7 @@ import scala.tools.nsc.util.{Position,NoPosition}
case UNBOX(tpe) =>
case SCOPE_ENTER(lv) =>
case SCOPE_EXIT(lv) =>
- case LOAD_EXCEPTION() =>
+ case LOAD_EXCEPTION(clasz) =>
*/
@@ -102,7 +102,7 @@ trait Opcodes { self: ICodes =>
def mayThrow(i: Instruction): Boolean = i match {
case LOAD_LOCAL(_) | STORE_LOCAL(_) | CONSTANT(_) | THIS(_) | CZJUMP(_, _, _, _)
- | DROP(_) | DUP(_) | RETURN(_) | LOAD_EXCEPTION() | JUMP(_) | CJUMP(_, _, _, _) => false
+ | DROP(_) | DUP(_) | RETURN(_) | LOAD_EXCEPTION(_) | JUMP(_) | CJUMP(_, _, _, _) => false
case _ => true
}
@@ -125,6 +125,7 @@ trait Opcodes { self: ICodes =>
* ->: ...:constant
*/
case class CONSTANT(constant: Constant) extends Instruction {
+ override def toString = "CONSTANT(" + constant.escapedStringValue + ")"
override def consumed = 0
override def produced = 1
@@ -299,6 +300,9 @@ trait Opcodes { self: ICodes =>
*
*/
case class CALL_METHOD(method: Symbol, style: InvokeStyle) extends Instruction with ReferenceEquality {
+ def toShortString =
+ "CALL_METHOD " + method.name +" ("+style+")"
+
/** Returns a string representation of this instruction */
override def toString(): String =
"CALL_METHOD " + method.fullName +" ("+style+")"
@@ -492,9 +496,12 @@ trait Opcodes { self: ICodes =>
* Stack: ...:Throwable(Ref)
* ->: ...:
*/
- case class THROW() extends Instruction {
- /** Returns a string representation of this instruction */
- override def toString(): String ="THROW"
+ case class THROW(clasz: Symbol) extends Instruction {
+ /** PP to ID: We discussed parameterizing LOAD_EXCEPTION but
+ * not THROW, which came about organically. It seems like the
+ * right thing, but can you confirm?
+ */
+ override def toString = "THROW(" + clasz.name + ")"
override def consumed = 1
override def produced = 0
@@ -570,11 +577,10 @@ trait Opcodes { self: ICodes =>
* Note: Unlike other instructions, it consumes all elements on the stack!
* then pushes one exception instance.
*/
- case class LOAD_EXCEPTION() extends Instruction {
- override def toString(): String = "LOAD_EXCEPTION"
+ case class LOAD_EXCEPTION(clasz: Symbol) extends Instruction {
override def consumed = error("LOAD_EXCEPTION does clean the whole stack, no idea how many things it consumes!")
override def produced = 1
- override def producedTypes = ThrowableReference :: Nil
+ override def producedTypes = REFERENCE(clasz) :: Nil
}
/** This class represents a method invocation style. */
@@ -598,10 +604,8 @@ trait Opcodes { self: ICodes =>
/** Is this an instance method call? */
def hasInstance: Boolean = this match {
- case Dynamic => true
- case Static(onInstance) => onInstance
- case SuperCall(_) => true
- case _ => false
+ case Static(false) => false
+ case _ => true
}
/** Returns a string representation of this style. */
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala
index 4fbec28fdf..edb42cb3fe 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala
@@ -125,7 +125,7 @@ trait Printers { self: ICodes =>
def printInstruction(i: Instruction) {
// if (settings.Xdce.value)
// print(if (i.useful) " " else " * ");
- if (i.pos.isDefined) print(i.pos.line.toString + "\t") else print("undef\t")
+ if (i.pos.isDefined) print(i.pos.line.toString + "\t") else print("?\t")
println(i.toString())
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
index 8909171587..da6625d037 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
@@ -26,7 +26,7 @@ package icode
trait TypeKinds { self: ICodes =>
import global._
import definitions.{ ArrayClass, AnyRefClass, ObjectClass, NullClass, NothingClass, arrayType }
- import icodes.checkerDebug
+ import icodes.{ checkerDebug, NothingReference, NullReference }
/** A map from scala primitive Types to ICode TypeKinds */
lazy val primitiveTypeMap: Map[Symbol, TypeKind] = {
@@ -68,12 +68,12 @@ trait TypeKinds { self: ICodes =>
def isBoxedType = false
final def isRefOrArrayType = isReferenceType || isArrayType
final def isRefArrayOrBoxType = isRefOrArrayType || isBoxedType
- final def isNothingType = this == REFERENCE(NothingClass)
+ final def isNothingType = this == NothingReference
+ final def isNullType = this == NullReference
final def isInterfaceType = this match {
- case REFERENCE(cls) if cls.isInterface => true
- case _ => false
+ case REFERENCE(cls) if cls.isInterface || cls.isTrait => true
+ case _ => false
}
- final def isBoxOrInterfaceType = isBoxedType || isInterfaceType
/** On the JVM, these types are like Ints for the
* purposes of calculating the lub.
@@ -150,21 +150,15 @@ trait TypeKinds { self: ICodes =>
import definitions._
val tp = global.lub(List(tk1.toType, tk2.toType))
val (front, rest) = tp.parents span (_.typeSymbol.hasTraitFlag)
- if (front.isEmpty || rest.isEmpty) tp
+
+ if (front.isEmpty) tp
+ else if (rest.isEmpty) front.head // all parents are interfaces
else rest.head match {
- case AnyClass | AnyRefClass | AnyValClass => tp
- case x => x
+ case AnyRefClass | ObjectClass => tp
+ case x => x
}
}
- // Approximate the JVM view of subtyping by collapsing boxed
- // values and interfaces into AnyRef. If we try to be more precise
- // with intersections we run into post-erasure issues where a type
- // like A with B may be used as an A in one place and a B in another.
- def isBoxLub = (
- (a.isBoxOrInterfaceType && b.isRefArrayOrBoxType) ||
- (b.isBoxOrInterfaceType && a.isRefArrayOrBoxType)
- )
def isIntLub = (
(a == INT && b.isIntSizedType) ||
(b == INT && a.isIntSizedType)
@@ -173,9 +167,13 @@ trait TypeKinds { self: ICodes =>
if (a == b) a
else if (a.isNothingType) b
else if (b.isNothingType) a
- else if (isBoxLub) AnyRefReference
+ else if (a.isBoxedType || b.isBoxedType) AnyRefReference // we should do better
else if (isIntLub) INT
- else if (a.isRefOrArrayType && b.isRefOrArrayType) toTypeKind(lub0(a, b))
+ else if (a.isRefOrArrayType && b.isRefOrArrayType) {
+ if (a.isNullType) b
+ else if (b.isNullType) a
+ else toTypeKind(lub0(a, b))
+ }
else throw new CheckerException("Incompatible types: " + a + " with " + b)
}
@@ -331,19 +329,16 @@ trait TypeKinds { self: ICodes =>
/** A boxed value. */
case class BOXED(kind: TypeKind) extends TypeKind {
override def isBoxedType = true
- /**
- * Approximate `lub'. The common type of two references is
- * always AnyRef. For 'real' least upper bound wrt to subclassing
- * use method 'lub'.
- */
+
override def maxType(other: TypeKind) = other match {
+ case BOXED(`kind`) => this
case REFERENCE(_) | ARRAY(_) | BOXED(_) => AnyRefReference
case _ => uncomparable("BOXED", other)
}
/** Checks subtyping relationship. */
override def <:<(other: TypeKind) = other match {
- case BOXED(other) => kind == other
+ case BOXED(`kind`) => true
case REFERENCE(AnyRefClass | ObjectClass) => true // TODO: platform dependent!
case _ => false
}
@@ -387,6 +382,9 @@ trait TypeKinds { self: ICodes =>
case ClassInfoType(_, _, sym) => primitiveOrRefType(sym)
case ExistentialType(_, t) => toTypeKind(t)
case AnnotatedType(_, t, _) => toTypeKind(t)
+ // PP to ID: I added RefinedType here, is this OK or should they never be
+ // allowed to reach here?
+ case RefinedType(parents, _) => parents map toTypeKind reduceLeft lub
// bq: useful hack when wildcard types come here
// case WildcardType => REFERENCE(ObjectClass)
case norm => abort(
@@ -405,12 +403,23 @@ trait TypeKinds { self: ICodes =>
assert(sym.isType, sym) // it must be compiling Array[a]
ObjectReference
}
- /** Interfaces are all treated as AnyRef else we will run into
- * post-erasure inconsistencies when differing lubs are needed.
+ /** Interfaces have to be handled delicately to avoid introducing
+ * spurious errors, but if we treat them all as AnyRef we lose too
+ * much information.
*/
- private def newReference(sym: Symbol) =
- if (sym.isInterface || sym.isTrait) ObjectReference
- else REFERENCE(sym)
+ private def newReference(sym: Symbol): TypeKind = {
+ // Can't call .toInterface (at this phase) or we trip an assertion.
+ // See PackratParser#grow for a method which fails with an apparent mismatch
+ // between "object PackratParsers$class" and "trait PackratParsers"
+ if (sym.isImplClass) {
+ // pos/spec-List.scala is the sole failure if we don't check for NoSymbol
+ val traitSym = sym.owner.info.decl(nme.interfaceName(sym.name))
+ if (traitSym != NoSymbol)
+ return REFERENCE(traitSym)
+ }
+ REFERENCE(sym)
+ }
+
private def primitiveOrRefType(sym: Symbol) =
primitiveTypeMap.getOrElse(sym, newReference(sym))
private def primitiveOrClassType(sym: Symbol, targs: List[Type]) =
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala
index b026921ca6..5ac44380f2 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala
@@ -406,7 +406,7 @@ abstract class CopyPropagation {
if (kind != UNIT)
out.stack = out.stack.drop(1)
- case THROW() =>
+ case THROW(_) =>
out.stack = out.stack.drop(1)
case DROP(kind) =>
@@ -424,7 +424,7 @@ abstract class CopyPropagation {
case SCOPE_ENTER(_) | SCOPE_EXIT(_) =>
()
- case LOAD_EXCEPTION() =>
+ case LOAD_EXCEPTION(_) =>
out.stack = Unknown :: Nil
case _ =>
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala
index c0de380b02..d7e5e84a80 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala
@@ -121,15 +121,15 @@ abstract class ReachingDefinitions {
var stackOut: List[Set[(BasicBlock, Int)]] = Nil
for ((instr, idx) <- b.toList.zipWithIndex) {
- if (instr == LOAD_EXCEPTION())
- ()
- else if (instr.consumed > depth) {
- drops = drops + (instr.consumed - depth)
- depth = 0
- stackOut = Nil
- } else {
- stackOut = stackOut.drop(instr.consumed)
- depth = depth - instr.consumed
+ instr match {
+ case LOAD_EXCEPTION(_) => ()
+ case _ if instr.consumed > depth =>
+ drops += (instr.consumed - depth)
+ depth = 0
+ stackOut = Nil
+ case _ =>
+ stackOut = stackOut.drop(instr.consumed)
+ depth -= instr.consumed
}
var prod = instr.produced
depth = depth + prod
@@ -178,7 +178,7 @@ abstract class ReachingDefinitions {
case STORE_LOCAL(l1) =>
locals = updateReachingDefinition(b, idx, locals)
stack = stack.drop(instr.consumed)
- case LOAD_EXCEPTION() =>
+ case LOAD_EXCEPTION(_) =>
stack = Nil
case _ =>
stack = stack.drop(instr.consumed)
@@ -211,8 +211,9 @@ abstract class ReachingDefinitions {
if (prod > d) {
res = (bb, i) :: res
n = n - (prod - d)
- if (instrs(i) != LOAD_EXCEPTION()) {
- d = instrs(i).consumed
+ instrs(i) match {
+ case LOAD_EXCEPTION(_) => ()
+ case _ => d = instrs(i).consumed
}
} else {
d -= prod
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
index b26b799f6d..ec36e50205 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
@@ -16,7 +16,7 @@ import scala.collection.{mutable, immutable}
abstract class TypeFlowAnalysis {
val global: Global
import global._
- import definitions.{ ObjectClass, NothingClass, AnyRefClass, StringClass }
+ import definitions.{ ObjectClass, NothingClass, AnyRefClass, StringClass, ThrowableClass }
/** The lattice of ICode types.
*/
@@ -39,9 +39,9 @@ abstract class TypeFlowAnalysis {
import icodes._
type Elem = TypeStack
- val top = new TypeStack
- val bottom = new TypeStack
- val exceptionHandlerStack: TypeStack = new TypeStack(List(REFERENCE(AnyRefClass)))
+ val top = new TypeStack
+ val bottom = new TypeStack
+ val exceptionHandlerStack = new TypeStack(List(REFERENCE(AnyRefClass)))
def lub2(exceptional: Boolean)(s1: TypeStack, s2: TypeStack) = {
if (s1 eq bottom) s2
@@ -144,9 +144,12 @@ abstract class TypeFlowAnalysis {
*/ }
out(b) = typeFlowLattice.bottom
}
- m.exh map (_.startBlock) filterNot (in contains _) foreach { start =>
- worklist += start
- in(start) = lattice.IState(in(start).vars, typeStackLattice.exceptionHandlerStack)
+ for (handler <- m.exh) {
+ val start = handler.startBlock
+ if (!in.contains(start)) {
+ worklist += start
+ in(start) = lattice.IState(in(start).vars, typeStackLattice.exceptionHandlerStack)
+ }
}
}
}
@@ -351,7 +354,7 @@ abstract class TypeFlowAnalysis {
case SCOPE_ENTER(_) | SCOPE_EXIT(_) =>
()
- case LOAD_EXCEPTION() =>
+ case LOAD_EXCEPTION(_) =>
stack.pop(stack.length)
stack.push(typeLattice.Object)
@@ -517,7 +520,7 @@ abstract class TypeFlowAnalysis {
if (kind != UNIT)
stack.pop;
- case THROW() =>
+ case THROW(_) =>
stack.pop
case DROP(kind) =>
@@ -535,7 +538,7 @@ abstract class TypeFlowAnalysis {
case SCOPE_ENTER(_) | SCOPE_EXIT(_) =>
()
- case LOAD_EXCEPTION() =>
+ case LOAD_EXCEPTION(_) =>
stack.pop(stack.length)
stack.push(typeLattice.top)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 1c0d80b0c7..160ce03a7f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -1393,7 +1393,7 @@ abstract class GenJVM extends SubComponent {
case RETURN(kind) =>
jcode emitRETURN javaType(kind)
- case THROW() =>
+ case THROW(_) =>
jcode.emitATHROW()
case DROP(kind) =>
@@ -1429,7 +1429,7 @@ abstract class GenJVM extends SubComponent {
} else
assert(false, "Illegal local var nesting: " + method)
- case LOAD_EXCEPTION() =>
+ case LOAD_EXCEPTION(_) =>
()
}
diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
index ad60524485..4268d999a0 100644
--- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
+++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
@@ -809,7 +809,7 @@ abstract class GenMSIL extends SubComponent {
NEW(REFERENCE(definitions.ThrowableClass)),
DUP(REFERENCE(definitions.ObjectClass)),
CALL_METHOD(definitions.ThrowableClass.primaryConstructor, Static(true)),
- THROW()
+ THROW(definitions.ThrowableClass)
))
b.close
endExBlock(b) = rest
@@ -1321,7 +1321,7 @@ abstract class GenMSIL extends SubComponent {
mcode.Emit(OpCodes.Leave, label)
}
- case THROW() =>
+ case THROW(_) =>
mcode.Emit(OpCodes.Throw)
case DROP(kind) =>
@@ -1340,7 +1340,7 @@ abstract class GenMSIL extends SubComponent {
case MONITOR_EXIT() =>
mcode.Emit(OpCodes.Call, MMONITOR_EXIT)
- case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION() =>
+ case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION(_) =>
()
}
diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
index 43eebcb0ad..6f8448ad0a 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
@@ -103,8 +103,8 @@ abstract class DeadCodeElimination extends SubComponent {
defs = defs + Pair(((bb, idx)), rd.vars)
// Console.println(i + ": " + (bb, idx) + " rd: " + rd + " and having: " + defs)
case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) |
- THROW() | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) |
- LOAD_EXCEPTION() | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => worklist += ((bb, idx))
+ THROW(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) |
+ LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => worklist += ((bb, idx))
case CALL_METHOD(m1, _) if isSideEffecting(m1) => worklist += ((bb, idx)); log("marking " + m1)
case CALL_METHOD(m1, SuperCall(_)) =>
worklist += ((bb, idx)) // super calls to constructor
@@ -113,7 +113,7 @@ abstract class DeadCodeElimination extends SubComponent {
val (bb1, idx1) = p
bb1(idx1) match {
case CALL_METHOD(m1, _) if isSideEffecting(m1) => true
- case LOAD_EXCEPTION() | DUP(_) | LOAD_MODULE(_) => true
+ case LOAD_EXCEPTION(_) | DUP(_) | LOAD_MODULE(_) => true
case _ =>
dropOf((bb1, idx1)) = (bb, idx)
// println("DROP is innessential: " + i + " because of: " + bb1(idx1) + " at " + bb1 + ":" + idx1)
@@ -168,7 +168,7 @@ abstract class DeadCodeElimination extends SubComponent {
log("added closure class for field " + sym)
liveClosures += sym.owner
- case LOAD_EXCEPTION() =>
+ case LOAD_EXCEPTION(_) =>
()
case _ =>
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index c15a4279fb..a59bc776aa 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -8,7 +8,7 @@ package scala.tools
package nsc
package settings
-import io.{AbstractFile, VirtualDirectory}
+import io.{ AbstractFile, VirtualDirectory }
import scala.tools.util.StringOps
import scala.collection.mutable.ListBuffer
import scala.io.Source
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 7156da076f..be6d886537 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -106,7 +106,6 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options")
val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after")
val check = PhasesSetting ("-Ycheck", "Check the tree at the end of")
- val checkDebug = BooleanSetting ("-Ycheck-debug", "Lots of extra output for -Ycheck")
val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination")
val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees")
val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL")
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 6a9cdf6961..ef80177464 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -10,6 +10,7 @@ import scala.collection.mutable.{HashMap, HashSet}
import scala.tools.nsc.util.NoPosition
import Flags._
import PartialFunction._
+import classfile.ClassfileConstants
trait Definitions extends reflect.generic.StandardDefinitions {
self: SymbolTable =>
@@ -71,17 +72,17 @@ trait Definitions extends reflect.generic.StandardDefinitions {
lazy val ObjectClass = getClass(sn.Object)
// bottom types
- lazy val NullClass = newClass(ScalaPackageClass, nme.Null, anyrefparam) setFlag (ABSTRACT | TRAIT | FINAL)
- lazy val NothingClass = newClass(ScalaPackageClass, nme.Nothing, anyparam) setFlag (ABSTRACT | TRAIT | FINAL)
- lazy val RuntimeNothingClass = getClass("scala.runtime.Nothing$")
- lazy val RuntimeNullClass = getClass("scala.runtime.Null$")
+ lazy val NullClass = newClass(ScalaPackageClass, nme.Null, anyrefparam) setFlag (ABSTRACT | TRAIT | FINAL)
+ lazy val NothingClass = newClass(ScalaPackageClass, nme.Nothing, anyparam) setFlag (ABSTRACT | TRAIT | FINAL)
+ lazy val RuntimeNothingClass = getClass(ClassfileConstants.SCALA_NOTHING)
+ lazy val RuntimeNullClass = getClass(ClassfileConstants.SCALA_NULL)
lazy val AnyValCompanionClass = getClass("scala.runtime.AnyValCompanion").setFlag(SEALED | ABSTRACT | TRAIT)
// the scala value classes
lazy val UnitClass =
newClass(ScalaPackageClass, nme.Unit, anyvalparam).setFlag(ABSTRACT | FINAL)
- import classfile.ClassfileConstants._
+ import ClassfileConstants._
lazy val ByteClass = newValueClass(nme.Byte, BYTE_TAG, 2)
lazy val ShortClass = newValueClass(nme.Short, SHORT_TAG, 4)
@@ -107,13 +108,14 @@ trait Definitions extends reflect.generic.StandardDefinitions {
)
// exceptions and other throwables
- lazy val ThrowableClass = getClass(sn.Throwable)
- lazy val NullPointerExceptionClass = getClass(sn.NPException)
- lazy val NonLocalReturnControlClass = getClass(sn.NLRControl)
+ lazy val ClassCastExceptionClass = getClass("java.lang.ClassCastException")
lazy val IndexOutOfBoundsExceptionClass = getClass(sn.IOOBException)
- lazy val UninitializedErrorClass = getClass("scala.UninitializedFieldError")
- lazy val MatchErrorClass = getClass("scala.MatchError")
lazy val InvocationTargetExceptionClass = getClass(sn.InvTargetException)
+ lazy val MatchErrorClass = getClass("scala.MatchError")
+ lazy val NonLocalReturnControlClass = getClass(sn.NLRControl)
+ lazy val NullPointerExceptionClass = getClass(sn.NPException)
+ lazy val ThrowableClass = getClass(sn.Throwable)
+ lazy val UninitializedErrorClass = getClass("scala.UninitializedFieldError")
// annotations
lazy val AnnotationClass = getClass("scala.Annotation")
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
index d22d5c1602..6de47e6c3d 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
@@ -31,9 +31,8 @@ abstract class ICodeReader extends ClassfileParser {
var staticCode: IClass = null // the ICode class static members
var method: IMethod = _ // the current IMethod
- val OBJECT: TypeKind = REFERENCE(definitions.ObjectClass)
- val nothingName = newTermName(SCALA_NOTHING)
- val nullName = newTermName(SCALA_NULL)
+ val nothingName = newTermName(ClassfileConstants.SCALA_NOTHING)
+ val nullName = newTermName(ClassfileConstants.SCALA_NULL)
var isScalaModule = false
/** Read back bytecode for the given class symbol. It returns
@@ -288,7 +287,7 @@ abstract class ICodeReader extends ClassfileParser {
if (local == 0 && !method.isStatic)
code.emit(THIS(method.symbol.owner));
else
- code.emit(LOAD_LOCAL(code.getLocal(local, OBJECT)));
+ code.emit(LOAD_LOCAL(code.getLocal(local, ObjectReference)));
case JVM.iload_0 => code.emit(LOAD_LOCAL(code.getLocal(0, INT)))
case JVM.iload_1 => code.emit(LOAD_LOCAL(code.getLocal(1, INT)))
@@ -310,16 +309,16 @@ abstract class ICodeReader extends ClassfileParser {
if (!method.isStatic)
code.emit(THIS(method.symbol.owner));
else
- code.emit(LOAD_LOCAL(code.getLocal(0, OBJECT)));
- case JVM.aload_1 => code.emit(LOAD_LOCAL(code.getLocal(1, OBJECT)))
- case JVM.aload_2 => code.emit(LOAD_LOCAL(code.getLocal(2, OBJECT)))
- case JVM.aload_3 => code.emit(LOAD_LOCAL(code.getLocal(3, OBJECT)))
+ code.emit(LOAD_LOCAL(code.getLocal(0, ObjectReference)));
+ case JVM.aload_1 => code.emit(LOAD_LOCAL(code.getLocal(1, ObjectReference)))
+ case JVM.aload_2 => code.emit(LOAD_LOCAL(code.getLocal(2, ObjectReference)))
+ case JVM.aload_3 => code.emit(LOAD_LOCAL(code.getLocal(3, ObjectReference)))
case JVM.iaload => code.emit(LOAD_ARRAY_ITEM(INT))
case JVM.laload => code.emit(LOAD_ARRAY_ITEM(LONG))
case JVM.faload => code.emit(LOAD_ARRAY_ITEM(FLOAT))
case JVM.daload => code.emit(LOAD_ARRAY_ITEM(DOUBLE))
- case JVM.aaload => code.emit(LOAD_ARRAY_ITEM(OBJECT))
+ case JVM.aaload => code.emit(LOAD_ARRAY_ITEM(ObjectReference))
case JVM.baload => code.emit(LOAD_ARRAY_ITEM(BYTE))
case JVM.caload => code.emit(LOAD_ARRAY_ITEM(CHAR))
case JVM.saload => code.emit(LOAD_ARRAY_ITEM(SHORT))
@@ -328,7 +327,7 @@ abstract class ICodeReader extends ClassfileParser {
case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, LONG))); size += 1
case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, FLOAT))); size += 1
case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, DOUBLE))); size += 1
- case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, OBJECT))); size += 1
+ case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, ObjectReference))); size += 1
case JVM.istore_0 => code.emit(STORE_LOCAL(code.getLocal(0, INT)))
case JVM.istore_1 => code.emit(STORE_LOCAL(code.getLocal(1, INT)))
case JVM.istore_2 => code.emit(STORE_LOCAL(code.getLocal(2, INT)))
@@ -347,24 +346,24 @@ abstract class ICodeReader extends ClassfileParser {
case JVM.dstore_3 => code.emit(STORE_LOCAL(code.getLocal(3, DOUBLE)))
case JVM.astore_0 =>
if (method.isStatic)
- code.emit(STORE_LOCAL(code.getLocal(0, OBJECT)))
+ code.emit(STORE_LOCAL(code.getLocal(0, ObjectReference)))
else
- code.emit(STORE_THIS(OBJECT))
- case JVM.astore_1 => code.emit(STORE_LOCAL(code.getLocal(1, OBJECT)))
- case JVM.astore_2 => code.emit(STORE_LOCAL(code.getLocal(2, OBJECT)))
- case JVM.astore_3 => code.emit(STORE_LOCAL(code.getLocal(3, OBJECT)))
+ code.emit(STORE_THIS(ObjectReference))
+ case JVM.astore_1 => code.emit(STORE_LOCAL(code.getLocal(1, ObjectReference)))
+ case JVM.astore_2 => code.emit(STORE_LOCAL(code.getLocal(2, ObjectReference)))
+ case JVM.astore_3 => code.emit(STORE_LOCAL(code.getLocal(3, ObjectReference)))
case JVM.iastore => code.emit(STORE_ARRAY_ITEM(INT))
case JVM.lastore => code.emit(STORE_ARRAY_ITEM(LONG))
case JVM.fastore => code.emit(STORE_ARRAY_ITEM(FLOAT))
case JVM.dastore => code.emit(STORE_ARRAY_ITEM(DOUBLE))
- case JVM.aastore => code.emit(STORE_ARRAY_ITEM(OBJECT))
+ case JVM.aastore => code.emit(STORE_ARRAY_ITEM(ObjectReference))
case JVM.bastore => code.emit(STORE_ARRAY_ITEM(BYTE))
case JVM.castore => code.emit(STORE_ARRAY_ITEM(CHAR))
case JVM.sastore => code.emit(STORE_ARRAY_ITEM(SHORT))
case JVM.pop => code.emit(DROP(INT)) // any 1-word type would do
case JVM.pop2 => code.emit(DROP(LONG)) // any 2-word type would do
- case JVM.dup => code.emit(DUP(OBJECT)) // TODO: Is the kind inside DUP ever needed?
+ case JVM.dup => code.emit(DUP(ObjectReference)) // TODO: Is the kind inside DUP ever needed?
case JVM.dup_x1 => code.emit(DUP_X1) // Predef.error("Unsupported JVM bytecode: dup_x1")
case JVM.dup_x2 => code.emit(DUP_X2) // Predef.error("Unsupported JVM bytecode: dup_x2")
case JVM.dup2 => code.emit(DUP(LONG)) // TODO: Is the kind inside DUP ever needed?
@@ -453,8 +452,8 @@ abstract class ICodeReader extends ClassfileParser {
case JVM.if_icmpge => code.emit(LCJUMP(parseJumpTarget, pc + size, GE, INT))
case JVM.if_icmpgt => code.emit(LCJUMP(parseJumpTarget, pc + size, GT, INT))
case JVM.if_icmple => code.emit(LCJUMP(parseJumpTarget, pc + size, LE, INT))
- case JVM.if_acmpeq => code.emit(LCJUMP(parseJumpTarget, pc + size, EQ, OBJECT))
- case JVM.if_acmpne => code.emit(LCJUMP(parseJumpTarget, pc + size, NE, OBJECT))
+ case JVM.if_acmpeq => code.emit(LCJUMP(parseJumpTarget, pc + size, EQ, ObjectReference))
+ case JVM.if_acmpne => code.emit(LCJUMP(parseJumpTarget, pc + size, NE, ObjectReference))
case JVM.goto => emit(LJUMP(parseJumpTarget))
case JVM.jsr => Predef.error("Cannot handle jsr/ret")
@@ -501,7 +500,7 @@ abstract class ICodeReader extends ClassfileParser {
case JVM.lreturn => code.emit(RETURN(LONG))
case JVM.freturn => code.emit(RETURN(FLOAT))
case JVM.dreturn => code.emit(RETURN(DOUBLE))
- case JVM.areturn => code.emit(RETURN(OBJECT))
+ case JVM.areturn => code.emit(RETURN(ObjectReference))
case JVM.return_ => code.emit(RETURN(UNIT))
case JVM.getstatic =>
@@ -562,8 +561,8 @@ abstract class ICodeReader extends ClassfileParser {
val tpe = pool.getClassOrArrayType(in.nextChar); size += 2
code.emit(CREATE_ARRAY(toTypeKind(tpe), 1))
- case JVM.arraylength => code.emit(CALL_PRIMITIVE(ArrayLength(OBJECT))); // the kind does not matter
- case JVM.athrow => code.emit(THROW());
+ case JVM.arraylength => code.emit(CALL_PRIMITIVE(ArrayLength(ObjectReference))); // the kind does not matter
+ case JVM.athrow => code.emit(THROW(definitions.ThrowableClass))
case JVM.checkcast =>
code.emit(CHECK_CAST(toTypeKind(pool.getClassOrArrayType(in.nextChar)))); size += 2
case JVM.instanceof =>
@@ -577,12 +576,12 @@ abstract class ICodeReader extends ClassfileParser {
case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, LONG))); size += 2
case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, FLOAT))); size += 2
case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, DOUBLE))); size += 2
- case JVM.aload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, OBJECT))); size += 2
+ case JVM.aload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, ObjectReference))); size += 2
case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, INT))); size += 2
case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, LONG))); size += 2
case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, FLOAT))); size += 2
case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, DOUBLE))); size += 2
- case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, OBJECT))); size += 2
+ case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, ObjectReference))); size += 2
case JVM.ret => Predef.error("Cannot handle jsr/ret")
case JVM.iinc =>
size += 4
@@ -600,8 +599,8 @@ abstract class ICodeReader extends ClassfileParser {
// assert(dim == 1, "Cannot handle multidimensional arrays yet.")
code.emit(CREATE_ARRAY(tpe, dim))
- case JVM.ifnull => code.emit(LCZJUMP(parseJumpTarget, pc + size, EQ, OBJECT))
- case JVM.ifnonnull => code.emit(LCZJUMP(parseJumpTarget, pc + size, NE, OBJECT))
+ case JVM.ifnull => code.emit(LCZJUMP(parseJumpTarget, pc + size, EQ, ObjectReference))
+ case JVM.ifnonnull => code.emit(LCZJUMP(parseJumpTarget, pc + size, NE, ObjectReference))
case JVM.goto_w => code.emit(LJUMP(parseJumpTargetW))
case JVM.jsr_w => Predef.error("Cannot handle jsr/ret")
@@ -739,7 +738,7 @@ abstract class ICodeReader extends ClassfileParser {
bb.emit(instr)
bb.close
- case THROW() =>
+ case THROW(clasz) =>
bb.emit(instr)
bb.close
diff --git a/test/files/pos/simple-exceptions.scala b/test/files/pos/simple-exceptions.scala
index 52c33fb43a..38f2fc8500 100644
--- a/test/files/pos/simple-exceptions.scala
+++ b/test/files/pos/simple-exceptions.scala
@@ -7,14 +7,14 @@ object Test {
def main(args: Array[String]): Unit = {
try {
try {
- Console.println("hi!");
- error("xx");
- } finally {
- Console.println("ho!")
+ Console.println("hi!")
+ error("xx")
}
- } catch {
+ finally Console.println("ho!")
+ }
+ catch {
case ex: IOException => Console.println("io exception!");
- case ex => Console.println(ex);
+ case ex => Console.println(ex);
}
}
}