diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala | 32 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 117 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 1 | ||||
-rw-r--r-- | test/files/jvm/t7006/Foo_1.flags | 2 | ||||
-rw-r--r-- | test/files/jvm/unreachable/Foo_1.flags | 1 | ||||
-rw-r--r-- | test/files/jvm/unreachable/Foo_1.scala | 110 | ||||
-rw-r--r-- | test/files/jvm/unreachable/Test.scala | 23 | ||||
-rw-r--r-- | test/files/run/inline-ex-handlers.check | 152 | ||||
-rw-r--r-- | test/files/run/unreachable.scala | 125 |
10 files changed, 451 insertions, 115 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index 917fe8b292..d772dcb6c4 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -17,7 +17,7 @@ trait BasicBlocks { self: ICodes => import opcodes._ - import global.{ settings, log, nme } + import global.{ settings, debuglog, log, nme } import nme.isExceptionResultName /** Override Array creation for efficiency (to not go through reflection). */ @@ -383,7 +383,6 @@ trait BasicBlocks { /** Close the block */ def close() { assert(!closed || ignore, this) - assert(instructionList.nonEmpty, "Empty block: " + this) if (ignore && closed) { // redundant `ignore &&` for clarity -- we should never be in state `!ignore && closed` // not doing anything to this block is important... // because the else branch reverses innocent blocks, which is wrong when they're in ignore mode (and closed) @@ -393,9 +392,38 @@ trait BasicBlocks { setFlag(DIRTYSUCCS) instructionList = instructionList.reverse instrs = instructionList.toArray + if (instructionList.isEmpty) { + debuglog(s"Removing empty block $this") + code removeBlock this + } } } + /** + * if cond is true, closes this block, entersIgnoreMode, and removes the block from + * its list of blocks. Used to allow a block to be started and then cancelled when it + * is discovered to be unreachable. + */ + def killIf(cond: Boolean) { + if (!settings.YdisableUnreachablePrevention.value && cond) { + debuglog(s"Killing block $this") + assert(instructionList.isEmpty, s"Killing a non empty block $this") + // only checked under debug because fetching predecessor list is moderately expensive + if (settings.debug.value) + assert(predecessors.isEmpty, s"Killing block $this which is referred to from ${predecessors.mkString}") + + close() + enterIgnoreMode() + } + } + + /** + * Same as killIf but with the logic of the condition reversed + */ + def killUnless(cond: Boolean) { + this killIf !cond + } + def open() { assert(closed, this) closed = false diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index ed458a4bbe..468e2cfd35 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -376,12 +376,14 @@ abstract class GenICode extends SubComponent { "I produce UNIT in a context where " + expectedType + " is expected!") // alternatives may be already closed by a tail-recursive jump + val contReachable = !(thenCtx.bb.ignore && elseCtx.bb.ignore) thenCtx.bb.closeWith(JUMP(contCtx.bb)) elseCtx.bb.closeWith( if (elsep == EmptyTree) JUMP(contCtx.bb) else JUMP(contCtx.bb) setPos tree.pos ) + contCtx.bb killUnless contReachable (contCtx, resKind) } private def genLoadTry(tree: Try, ctx: Context, setGeneratedType: TypeKind => Unit): Context = { @@ -477,7 +479,11 @@ abstract class GenICode extends SubComponent { val resCtx: Context = tree match { case LabelDef(name, params, rhs) => def genLoadLabelDef = { - val ctx1 = ctx.newBlock() + val ctx1 = ctx.newBlock() // note: we cannot kill ctx1 if ctx is in ignore mode because + // label defs can be the target of jumps from other locations. + // that means label defs can lead to unreachable code without + // proper reachability analysis + if (nme.isLoopHeaderLabel(name)) ctx1.bb.loopHeader = true @@ -560,6 +566,7 @@ abstract class GenICode extends SubComponent { // the list, otherwise infinite recursion happens for // finalizers that contain 'return' val fctx = finalizerCtx.newBlock() + fctx.bb killIf ctx1.bb.ignore ctx1.bb.closeWith(JUMP(fctx.bb)) ctx1 = genLoad(f1, fctx, UNIT) } @@ -949,6 +956,8 @@ abstract class GenICode extends SubComponent { debuglog("Generating SWITCH statement.") val ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue) val afterCtx = ctx1.newBlock() + afterCtx.bb killIf ctx1.bb.ignore + var afterCtxReachable = false var caseCtx: Context = null generatedType = toTypeKind(tree.tpe) @@ -959,6 +968,7 @@ abstract class GenICode extends SubComponent { for (caze @ CaseDef(pat, guard, body) <- cases) { assert(guard == EmptyTree, guard) val tmpCtx = ctx1.newBlock() + tmpCtx.bb killIf ctx1.bb.ignore pat match { case Literal(value) => tags = value.intValue :: tags @@ -980,12 +990,15 @@ abstract class GenICode extends SubComponent { } caseCtx = genLoad(body, tmpCtx, generatedType) + afterCtxReachable |= !caseCtx.bb.ignore // close the block unless it's already been closed by the body, which closes the block if it ends in a jump (which is emitted to have alternatives share their body) caseCtx.bb.closeWith(JUMP(afterCtx.bb) setPos caze.pos) } + afterCtxReachable |= (default == afterCtx) ctx1.bb.emitOnly( SWITCH(tags.reverse map (x => List(x)), (default :: targets).reverse) setPos tree.pos ) + afterCtx.bb killUnless afterCtxReachable afterCtx } genLoadMatch @@ -1342,9 +1355,9 @@ abstract class GenICode extends SubComponent { private def genCond(tree: Tree, ctx: Context, thenCtx: Context, - elseCtx: Context): Unit = - { - def genComparisonOp(l: Tree, r: Tree, code: Int) { + elseCtx: Context): Boolean = + { + def genComparisonOp(l: Tree, r: Tree, code: Int): Boolean = { val op: TestOp = code match { case scalaPrimitives.LT => LT case scalaPrimitives.LE => LE @@ -1360,27 +1373,33 @@ abstract class GenICode extends SubComponent { lazy val nonNullSide = ifOneIsNull(l, r) if (isReferenceEqualityOp(code) && nonNullSide != null) { val ctx1 = genLoad(nonNullSide, ctx, ObjectReference) + val branchesReachable = !ctx1.bb.ignore ctx1.bb.emitOnly( CZJUMP(thenCtx.bb, elseCtx.bb, op, ObjectReference) ) + branchesReachable } else { val kind = getMaxType(l.tpe :: r.tpe :: Nil) var ctx1 = genLoad(l, ctx, kind) ctx1 = genLoad(r, ctx1, kind) + val branchesReachable = !ctx1.bb.ignore ctx1.bb.emitOnly( CJUMP(thenCtx.bb, elseCtx.bb, op, kind) setPos r.pos ) + branchesReachable } } debuglog("Entering genCond with tree: " + tree) // the default emission - def default() = { + def default(): Boolean = { val ctx1 = genLoad(tree, ctx, BOOL) + val branchesReachable = !ctx1.bb.ignore ctx1.bb.closeWith(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL) setPos tree.pos) + branchesReachable } tree match { @@ -1392,11 +1411,12 @@ abstract class GenICode extends SubComponent { lazy val Select(lhs, _) = fun lazy val rhs = args.head - def genZandOrZor(and: Boolean) = { + def genZandOrZor(and: Boolean): Boolean = { val ctxInterm = ctx.newBlock() - if (and) genCond(lhs, ctx, ctxInterm, elseCtx) + val branchesReachable = if (and) genCond(lhs, ctx, ctxInterm, elseCtx) else genCond(lhs, ctx, thenCtx, ctxInterm) + ctxInterm.bb killUnless branchesReachable genCond(rhs, ctxInterm, thenCtx, elseCtx) } @@ -1436,7 +1456,7 @@ abstract class GenICode extends SubComponent { * @param thenCtx target context if the comparison yields true * @param elseCtx target context if the comparison yields false */ - def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context)(thenCtx: Context, elseCtx: Context): Unit = { + def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context)(thenCtx: Context, elseCtx: Context): Boolean = { def getTempLocal = ctx.method.lookupLocal(nme.EQEQ_LOCAL_VAR) getOrElse { ctx.makeLocal(l.pos, AnyRefClass.tpe, nme.EQEQ_LOCAL_VAR.toString) } @@ -1476,26 +1496,40 @@ abstract class GenICode extends SubComponent { val ctx1 = genLoad(l, ctx, ObjectReference) val ctx2 = genLoad(r, ctx1, ObjectReference) + val branchesReachable = !ctx2.bb.ignore ctx2.bb.emitOnly( CALL_METHOD(equalsMethod, if (settings.optimise.value) Dynamic else Static(onInstance = false)), CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL) ) + branchesReachable } else { - if (isNull(l)) + if (isNull(l)) { // null == expr -> expr eq null - genLoad(r, ctx, ObjectReference).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference) - else if (isNull(r)) { + val ctx1 = genLoad(r, ctx, ObjectReference) + val branchesReachable = !ctx1.bb.ignore + ctx1.bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference) + branchesReachable + } else if (isNull(r)) { // expr == null -> expr eq null - genLoad(l, ctx, ObjectReference).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference) + val ctx1 = genLoad(l, ctx, ObjectReference) + val branchesReachable = !ctx1.bb.ignore + ctx1.bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference) + branchesReachable } else { val eqEqTempLocal = getTempLocal var ctx1 = genLoad(l, ctx, ObjectReference) - lazy val nonNullCtx = ctx1.newBlock() + val branchesReachable = !ctx1.bb.ignore + lazy val nonNullCtx = { + val block = ctx1.newBlock() + block.bb killUnless branchesReachable + block + } // l == r -> if (l eq null) r eq null else l.equals(r) ctx1 = genLoad(r, ctx1, ObjectReference) val nullCtx = ctx1.newBlock() + nullCtx.bb killUnless branchesReachable ctx1.bb.emitOnly( STORE_LOCAL(eqEqTempLocal) setPos l.pos, @@ -1512,6 +1546,7 @@ abstract class GenICode extends SubComponent { CALL_METHOD(Object_equals, Dynamic), CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL) ) + branchesReachable } } } @@ -1957,6 +1992,7 @@ abstract class GenICode extends SubComponent { val outerCtx = this.dup // context for generating exception handlers, covered by the catch-all finalizer val finalizerCtx = this.dup // context for generating finalizer handler val normalExitCtx = outerCtx.newBlock() // context where flow will go on a "normal" (non-return, non-throw) exit from a try or catch handler + var normalExitReachable = false var tmp: Local = null val kind = toTypeKind(tree.tpe) val guardResult = kind != UNIT && mayCleanStack(finalizer) @@ -1971,6 +2007,7 @@ abstract class GenICode extends SubComponent { def emitFinalizer(ctx: Context): Context = if (!finalizer.isEmpty) { val ctx1 = finalizerCtx.dup.newBlock() + ctx1.bb killIf ctx.bb.ignore ctx.bb.closeWith(JUMP(ctx1.bb)) if (guardResult) { @@ -1986,32 +2023,38 @@ abstract class GenICode extends SubComponent { // Generate the catch-all exception handler that deals with uncaught exceptions coming // from the try or exception handlers. It catches the exception, runs the finally code, then rethrows // the exception - if (finalizer != EmptyTree) { - val exh = outerCtx.newExceptionHandler(NoSymbol, finalizer.pos) // finalizer covers exception handlers - this.addActiveHandler(exh) // .. and body aswell - val exhStartCtx = finalizerCtx.enterExceptionHandler(exh) - val exception = exhStartCtx.makeLocal(finalizer.pos, ThrowableClass.tpe, "exc") - loadException(exhStartCtx, exh, finalizer.pos) - exhStartCtx.bb.emit(STORE_LOCAL(exception)) - val exhEndCtx = genLoad(finalizer, exhStartCtx, UNIT) - exhEndCtx.bb.emit(LOAD_LOCAL(exception)) - exhEndCtx.bb.closeWith(THROW(ThrowableClass)) - exhEndCtx.bb.enterIgnoreMode() - finalizerCtx.endHandler() - } - - // Generate each exception handler - for ((sym, kind, handler) <- handlers) { - val exh = this.newExceptionHandler(sym, tree.pos) - val exhStartCtx = outerCtx.enterExceptionHandler(exh) - exhStartCtx.addFinalizer(finalizer, finalizerCtx) - loadException(exhStartCtx, exh, tree.pos) - val exhEndCtx = handler(exhStartCtx) - exhEndCtx.bb.closeWith(JUMP(normalExitCtx.bb)) - outerCtx.endHandler() + if (settings.YdisableUnreachablePrevention.value || !outerCtx.bb.ignore) { + if (finalizer != EmptyTree) { + val exh = outerCtx.newExceptionHandler(NoSymbol, finalizer.pos) // finalizer covers exception handlers + this.addActiveHandler(exh) // .. and body aswell + val exhStartCtx = finalizerCtx.enterExceptionHandler(exh) + exhStartCtx.bb killIf outerCtx.bb.ignore + val exception = exhStartCtx.makeLocal(finalizer.pos, ThrowableClass.tpe, "exc") + loadException(exhStartCtx, exh, finalizer.pos) + exhStartCtx.bb.emit(STORE_LOCAL(exception)) + val exhEndCtx = genLoad(finalizer, exhStartCtx, UNIT) + exhEndCtx.bb.emit(LOAD_LOCAL(exception)) + exhEndCtx.bb.closeWith(THROW(ThrowableClass)) + exhEndCtx.bb.enterIgnoreMode() + finalizerCtx.endHandler() + } + + // Generate each exception handler + for ((sym, kind, handler) <- handlers) { + val exh = this.newExceptionHandler(sym, tree.pos) + val exhStartCtx = outerCtx.enterExceptionHandler(exh) + exhStartCtx.bb killIf outerCtx.bb.ignore + exhStartCtx.addFinalizer(finalizer, finalizerCtx) + loadException(exhStartCtx, exh, tree.pos) + val exhEndCtx = handler(exhStartCtx) + normalExitReachable |= !exhEndCtx.bb.ignore + exhEndCtx.bb.closeWith(JUMP(normalExitCtx.bb)) + outerCtx.endHandler() + } } val bodyCtx = this.newBlock() + bodyCtx.bb killIf outerCtx.bb.ignore if (finalizer != EmptyTree) bodyCtx.addFinalizer(finalizer, finalizerCtx) @@ -2019,6 +2062,8 @@ abstract class GenICode extends SubComponent { outerCtx.bb.closeWith(JUMP(bodyCtx.bb)) + normalExitReachable |= !bodyEndCtx.bb.ignore + normalExitCtx.bb killUnless normalExitReachable bodyEndCtx.bb.closeWith(JUMP(normalExitCtx.bb)) emitFinalizer(normalExitCtx) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 703922b20a..1fb7b11b20 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -3298,7 +3298,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def normalize(m: IMethod) { if(!m.hasCode) { return } collapseJumpOnlyBlocks(m) - elimUnreachableBlocks(m) + if (settings.optimise.value) + elimUnreachableBlocks(m) icodes checkValid m } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 702071f906..2aee9bd4bc 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -173,6 +173,7 @@ trait ScalaSettings extends AbsScalaSettings val Yinvalidate = StringSetting ("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "") val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.") val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") + val YdisableUnreachablePrevention = BooleanSetting("-Ydisable-unreachable-prevention", "Disable the prevention of unreachable blocks in code generation.") val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() diff --git a/test/files/jvm/t7006/Foo_1.flags b/test/files/jvm/t7006/Foo_1.flags index b723a661a7..37b2116413 100644 --- a/test/files/jvm/t7006/Foo_1.flags +++ b/test/files/jvm/t7006/Foo_1.flags @@ -1 +1 @@ --Ynooptimise -Ydead-code -Ydebug -Xfatal-warnings +-optimise -Ydebug -Xfatal-warnings diff --git a/test/files/jvm/unreachable/Foo_1.flags b/test/files/jvm/unreachable/Foo_1.flags new file mode 100644 index 0000000000..ce6e93b3da --- /dev/null +++ b/test/files/jvm/unreachable/Foo_1.flags @@ -0,0 +1 @@ +-Ynooptimise
\ No newline at end of file diff --git a/test/files/jvm/unreachable/Foo_1.scala b/test/files/jvm/unreachable/Foo_1.scala new file mode 100644 index 0000000000..d17421c516 --- /dev/null +++ b/test/files/jvm/unreachable/Foo_1.scala @@ -0,0 +1,110 @@ +class Foo_1 { + def unreachableNormalExit: Int = { + return 42 + 0 + } + + def unreachableIf: Int = { + return 42 + if (util.Random.nextInt % 2 == 0) + 0 + else + 1 + } + + def unreachableIfBranches: Int = { + if (util.Random.nextInt % 2 == 0) + return 42 + else + return 42 + + return 0 + } + + def unreachableOneLegIf: Int = { + if (util.Random.nextInt % 2 == 0) + return 42 + + return 42 + } + + def unreachableLeftBranch: Int = { + val result = if (util.Random.nextInt % 2 == 0) + return 42 + else + 42 + + return result + } + + def unreachableRightBranch: Int = { + val result = if (util.Random.nextInt % 2 == 0) + 42 + else + return 42 + + return result + } + + def unreachableTryCatchFinally: Int = { + return 42 + try { + return 0 + } catch { + case x: Throwable => return 1 + } finally { + return 2 + } + return 3 + } + + def unreachableAfterTry: Int = { + try { + return 42 + } catch { + case x: Throwable => return 2 + } + return 3 + } + + def unreachableAfterCatch: Int = { + try { + error("haha") + } catch { + case x: Throwable => return 42 + } + return 3 + } + + def unreachableAfterFinally: Int = { + try { + return 1 + } catch { + case x: Throwable => return 2 + } finally { + return 42 + } + return 3 + } + + def unreachableSwitch: Int = { + return 42 + val x = util.Random.nextInt % 2 + x match { + case 0 => return 0 + case 1 => return 1 + case _ => error("wtf") + } + 2 + } + + def unreachableAfterSwitch: Int = { + val x = util.Random.nextInt % 2 + x match { + case 0 => return 42 + case 1 => return 41 + x + case _ => error("wtf") + } + 2 + } +}
\ No newline at end of file diff --git a/test/files/jvm/unreachable/Test.scala b/test/files/jvm/unreachable/Test.scala new file mode 100644 index 0000000000..3f520eb106 --- /dev/null +++ b/test/files/jvm/unreachable/Test.scala @@ -0,0 +1,23 @@ +import scala.tools.partest.BytecodeTest +import scala.tools.asm +import asm.tree.InsnList +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("Foo_1") + // Foo_1 is full of unreachable code which if not elimintated + // will result in NOPs as can be confirmed by adding -Ydisable-unreachable-prevention + // to Foo_1.flags + for (methodNode <- classNode.methods.asScala) { + val got = count(methodNode.instructions, asm.Opcodes.NOP) + if (got != 0) println(s"Found $got NOP(s) in ${methodNode.name}") + } + } + + def count(insnList: InsnList, opcode: Int): Int = { + def isNop(node: asm.tree.AbstractInsnNode): Boolean = + (node.getOpcode == opcode) + insnList.iterator.asScala.count(isNop) + } +}
\ No newline at end of file diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check index 0a234e2659..abcc8bf42d 100644 --- a/test/files/run/inline-ex-handlers.check +++ b/test/files/run/inline-ex-handlers.check @@ -14,9 +14,9 @@ < < 2: 247c246 -< blocks: [1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18] +< blocks: [1,2,3,4,5,6,7,8,11,12,13,14,15,16,17,18] --- -> blocks: [1,2,3,4,5,6,8,10,11,12,13,14,15,16,17,18] +> blocks: [1,2,3,4,5,6,8,11,12,13,14,15,16,17,18] 258,260d256 < 92 JUMP 7 < @@ -57,19 +57,18 @@ > ? LOAD_LOCAL(value x5) > 106 CALL_METHOD MyException.message (dynamic) 519c518 -< blocks: [1,2,3,4,6,7,8,9,10] +< blocks: [1,2,3,4,6,7,9,10] --- -> blocks: [1,2,3,4,6,7,8,9,10,11,12,13] -548c547 +> blocks: [1,3,4,6,7,9,10,11,12,13] +548c547,552 < 306 THROW(MyException) --- > ? JUMP 11 -549a549,553 +> > 11: > ? LOAD_LOCAL(variable monitor4) > 305 MONITOR_EXIT > ? JUMP 12 -> 554c558 < ? THROW(Throwable) --- @@ -85,7 +84,13 @@ > 304 MONITOR_EXIT > ? STORE_LOCAL(value t) > ? JUMP 13 -575a587,598 +574c585 +< 310 JUMP 2 +--- +> 300 RETURN(UNIT) +576c587,596 +< 2: +--- > 13: > 310 LOAD_MODULE object Predef > 310 CALL_PRIMITIVE(StartConcat) @@ -96,37 +101,34 @@ > 310 CALL_PRIMITIVE(StringConcat(REF(class String))) > 310 CALL_PRIMITIVE(EndConcat) > 310 CALL_METHOD scala.Predef.println (dynamic) -> 310 JUMP 2 -> -584c607 -< catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6 +584c604 +< catch (Throwable) in ArrayBuffer(7, 9, 10) starting at: 6 --- -> catch (Throwable) in ArrayBuffer(7, 8, 9, 10, 11) starting at: 6 -587c610 -< catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10) starting at: 3 +> catch (Throwable) in ArrayBuffer(7, 9, 10, 11) starting at: 6 +587c607 +< catch (Throwable) in ArrayBuffer(4, 6, 7, 9, 10) starting at: 3 --- -> catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10, 11, 12) starting at: 3 -619c642 +> catch (Throwable) in ArrayBuffer(4, 6, 7, 9, 10, 11, 12) starting at: 3 +619c639 < blocks: [1,3,4,5,6,8,9] --- > blocks: [1,3,4,5,6,8,9,10,11] -643c666,667 +643c663,669 < 78 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 10 -644a669,673 +> > 10: > 81 LOAD_LOCAL(value e) > ? STORE_LOCAL(variable exc1) > ? JUMP 11 -> -669c698,699 +669c695,696 < 81 THROW(Exception) --- > ? STORE_LOCAL(variable exc1) > ? JUMP 11 -685a716,728 +685a713,725 > 11: > 83 LOAD_MODULE object Predef > 83 CONSTANT("finally") @@ -140,19 +142,19 @@ > 84 LOAD_LOCAL(variable exc1) > 84 THROW(Throwable) > -691c734 +691c731 < catch (<none>) in ArrayBuffer(4, 5, 6, 8) starting at: 3 --- > catch (<none>) in ArrayBuffer(4, 5, 6, 8, 10) starting at: 3 -715c758 +715c755 < locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x -717c760 +717c757 < blocks: [1,3,4,5,6,9,13,14,15,18,20,21,23,24] --- > blocks: [1,3,4,5,6,9,13,14,15,18,20,21,23,24,25,26,27] -741c784,791 +741c781,788 < 172 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -163,64 +165,64 @@ > 170 STORE_LOCAL(value x4) > 170 SCOPE_ENTER value x4 > 170 JUMP 14 -781,784d830 +781,784d827 < 175 LOAD_LOCAL(value x5) < 175 CALL_METHOD MyException.message (dynamic) < 175 STORE_LOCAL(value message) < 175 SCOPE_ENTER value message -786c832,833 +786c829,830 < 176 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 176 CALL_METHOD MyException.message (dynamic) -790c837,838 +790c834,835 < 177 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 177 CALL_METHOD MyException.message (dynamic) -792c840,841 +792c837,838 < 177 THROW(MyException) --- > ? STORE_LOCAL(value ex6) > ? JUMP 26 -796c845,846 +796c842,843 < 170 THROW(Throwable) --- > ? STORE_LOCAL(value ex6) > ? JUMP 26 -805a856,861 +805a853,858 > 26: > 169 LOAD_LOCAL(value ex6) > 169 STORE_LOCAL(value x4) > 169 SCOPE_ENTER value x4 > 169 JUMP 5 > -816,819d871 +816,819d868 < 180 LOAD_LOCAL(value x5) < 180 CALL_METHOD MyException.message (dynamic) < 180 STORE_LOCAL(value message) < 180 SCOPE_ENTER value message -821c873,874 +821c870,871 < 181 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 181 CALL_METHOD MyException.message (dynamic) -825c878,879 +825c875,876 < 182 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 182 CALL_METHOD MyException.message (dynamic) -827c881,882 +827c878,879 < 182 THROW(MyException) --- > ? STORE_LOCAL(variable exc2) > ? JUMP 27 -831c886,887 +831c883,884 < 169 THROW(Throwable) --- > ? STORE_LOCAL(variable exc2) > ? JUMP 27 -847a904,916 +847a901,913 > 27: > 184 LOAD_MODULE object Predef > 184 CONSTANT("finally") @@ -234,23 +236,23 @@ > 185 LOAD_LOCAL(variable exc2) > 185 THROW(Throwable) > -853c922 +853c919 < catch (Throwable) in ArrayBuffer(13, 14, 15, 18, 20, 21, 23) starting at: 4 --- > catch (Throwable) in ArrayBuffer(13, 14, 15, 18, 20, 21, 23, 25) starting at: 4 -856c925 +856c922 < catch (<none>) in ArrayBuffer(4, 5, 6, 9, 13, 14, 15, 18, 20, 21, 23) starting at: 3 --- > catch (<none>) in ArrayBuffer(4, 5, 6, 9, 13, 14, 15, 18, 20, 21, 23, 25, 26) starting at: 3 -880c949 +880c946 < locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value e, value ex6, value x4, value x5, value x -882c951 +882c948 < blocks: [1,2,3,6,7,8,11,13,14,16] --- > blocks: [1,2,3,6,7,8,11,13,14,16,17] -906c975,982 +906c972,979 < 124 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -261,29 +263,29 @@ > 122 STORE_LOCAL(value x4) > 122 SCOPE_ENTER value x4 > 122 JUMP 7 -931,934d1006 +931,934d1003 < 127 LOAD_LOCAL(value x5) < 127 CALL_METHOD MyException.message (dynamic) < 127 STORE_LOCAL(value message) < 127 SCOPE_ENTER value message -936c1008,1009 +936c1005,1006 < 127 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 127 CALL_METHOD MyException.message (dynamic) -965c1038 +965c1035 < catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16) starting at: 3 --- > catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16, 17) starting at: 3 -989c1062 +989c1059 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e --- > locals: value args, variable result, value ex6, value x4, value x5, value x, value e -991c1064 +991c1061 < blocks: [1,2,3,4,5,8,12,13,14,16] --- > blocks: [1,2,3,5,8,12,13,14,16,17] -1015c1088,1097 +1015c1085,1094 < 148 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -296,25 +298,25 @@ > 154 LOAD_LOCAL(value x4) > 154 IS_INSTANCE REF(class MyException) > 154 CZJUMP (BOOL)NE ? 5 : 8 -1036,1038d1117 +1036,1038d1114 < 145 JUMP 4 < < 4: -1048,1051d1126 +1048,1051d1123 < 154 LOAD_LOCAL(value x5) < 154 CALL_METHOD MyException.message (dynamic) < 154 STORE_LOCAL(value message) < 154 SCOPE_ENTER value message -1053c1128,1129 +1053c1125,1126 < 154 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 154 CALL_METHOD MyException.message (dynamic) -1270c1346 +1270c1343 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1294c1370,1377 +1294c1367,1374 < 38 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) @@ -325,20 +327,20 @@ > 42 CONSTANT("IllegalArgumentException") > 42 CALL_METHOD scala.Predef.println (dynamic) > 42 JUMP 2 -1341c1424 +1341c1421 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, value x4, value x5, value x -1343c1426 +1343c1423 < blocks: [1,2,3,4,5,8,10,11,13,14,16] --- > blocks: [1,2,3,5,8,10,11,13,14,16,17] -1367c1450,1451 +1367c1447,1448 < 203 THROW(MyException) --- > ? STORE_LOCAL(value ex6) > ? JUMP 17 -1387c1471,1480 +1387c1468,1477 < 209 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -351,41 +353,41 @@ > 212 LOAD_LOCAL(value x4) > 212 IS_INSTANCE REF(class MyException) > 212 CZJUMP (BOOL)NE ? 5 : 8 -1400,1402d1492 +1400,1402d1489 < 200 JUMP 4 < < 4: -1412,1415d1501 +1412,1415d1498 < 212 LOAD_LOCAL(value x5) < 212 CALL_METHOD MyException.message (dynamic) < 212 STORE_LOCAL(value message) < 212 SCOPE_ENTER value message -1417c1503,1504 +1417c1500,1501 < 213 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 213 CALL_METHOD MyException.message (dynamic) -1461c1548 +1461c1545 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1485c1572,1573 +1485c1569,1570 < 58 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 8 -1486a1575,1580 +1486a1572,1577 > 8: > 62 LOAD_MODULE object Predef > 62 CONSTANT("RuntimeException") > 62 CALL_METHOD scala.Predef.println (dynamic) > 62 JUMP 2 > -1534c1628 -< blocks: [1,2,3,4] +1534c1625 +< blocks: [1,3,4] --- -> blocks: [1,2,3,4,5] -1554c1648,1653 +> blocks: [1,3,4,5] +1554c1645,1650 < 229 THROW(MyException) --- > ? JUMP 5 @@ -394,19 +396,19 @@ > ? LOAD_LOCAL(variable monitor1) > 228 MONITOR_EXIT > 228 THROW(Throwable) -1560c1659 +1560c1656 < ? THROW(Throwable) --- > 228 THROW(Throwable) -1588c1687 +1588c1684 < locals: value args, variable result, variable monitor2, variable monitorResult1 --- > locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 -1590c1689 -< blocks: [1,2,3,4] +1590c1686 +< blocks: [1,3,4] --- -> blocks: [1,2,3,4,5] -1613c1712,1720 +> blocks: [1,3,4,5] +1613c1709,1717 < 245 THROW(MyException) --- > ? STORE_LOCAL(value exception$1) @@ -418,7 +420,7 @@ > ? LOAD_LOCAL(variable monitor2) > 244 MONITOR_EXIT > 244 THROW(Throwable) -1619c1726 +1619c1723 < ? THROW(Throwable) --- > 244 THROW(Throwable) diff --git a/test/files/run/unreachable.scala b/test/files/run/unreachable.scala new file mode 100644 index 0000000000..d3b9f3404f --- /dev/null +++ b/test/files/run/unreachable.scala @@ -0,0 +1,125 @@ +object Test extends App { + def unreachableNormalExit: Int = { + return 42 + 0 + } + + def unreachableIf: Int = { + return 42 + if (util.Random.nextInt % 2 == 0) + 0 + else + 1 + } + + def unreachableIfBranches: Int = { + if (util.Random.nextInt % 2 == 0) + return 42 + else + return 42 + + return 0 + } + + def unreachableOneLegIf: Int = { + if (util.Random.nextInt % 2 == 0) + return 42 + + return 42 + } + + def unreachableLeftBranch: Int = { + val result = if (util.Random.nextInt % 2 == 0) + return 42 + else + 42 + + return result + } + + def unreachableRightBranch: Int = { + val result = if (util.Random.nextInt % 2 == 0) + 42 + else + return 42 + + return result + } + + def unreachableTryCatchFinally: Int = { + return 42 + try { + return 0 + } catch { + case x: Throwable => return 1 + } finally { + return 2 + } + return 3 + } + + def unreachableAfterTry: Int = { + try { + return 42 + } catch { + case x: Throwable => return 2 + } + return 3 + } + + def unreachableAfterCatch: Int = { + try { + error("haha") + } catch { + case x: Throwable => return 42 + } + return 3 + } + + def unreachableAfterFinally: Int = { + try { + return 1 + } catch { + case x: Throwable => return 2 + } finally { + return 42 + } + return 3 + } + + def unreachableSwitch: Int = { + return 42 + val x = util.Random.nextInt % 2 + x match { + case 0 => return 0 + case 1 => return 1 + case _ => error("wtf") + } + 2 + } + + def unreachableAfterSwitch: Int = { + val x = util.Random.nextInt % 2 + x match { + case 0 => return 42 + case 1 => return 41 + x + case _ => error("wtf") + } + 2 + } + + def check(f: Int) = assert(f == 42, s"Expected 42 but got $f") + + check(unreachableNormalExit) + check(unreachableIf) + check(unreachableIfBranches) + check(unreachableOneLegIf) + check(unreachableLeftBranch) + check(unreachableRightBranch) + check(unreachableTryCatchFinally) + check(unreachableAfterTry) + check(unreachableAfterCatch) + check(unreachableAfterFinally) + check(unreachableSwitch) + check(unreachableAfterSwitch) +}
\ No newline at end of file |