diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LoopExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LoopExpressionGenerator.kt index 0337ac67023..542787d2821 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LoopExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LoopExpressionGenerator.kt @@ -47,14 +47,18 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen } fun generateBreak(ktBreak: KtBreakExpression): IrExpression { - val parentLoop = findParentLoop(ktBreak) + val parentLoop = findParentLoop(ktBreak) ?: + return ErrorExpressionGenerator(statementGenerator).generateErrorExpression( + ktBreak, RuntimeException("Loop not found for break expression: ${ktBreak.text}")) return IrBreakImpl(ktBreak.startOffset, ktBreak.endOffset, context.builtIns.nothingType, parentLoop).apply { label = ktBreak.getLabelName() } } fun generateContinue(ktContinue: KtContinueExpression): IrExpression { - val parentLoop = findParentLoop(ktContinue) + val parentLoop = findParentLoop(ktContinue) ?: + return ErrorExpressionGenerator(statementGenerator).generateErrorExpression( + ktContinue, RuntimeException("Loop not found for continue expression: ${ktContinue.text}")) return IrContinueImpl(ktContinue.startOffset, ktContinue.endOffset, context.builtIns.nothingType, parentLoop).apply { label = ktContinue.getLabelName() } @@ -63,10 +67,10 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen private fun getLoopLabel(ktLoop: KtLoopExpression): String? = (ktLoop.parent as? KtLabeledExpression)?.getLabelName() - private fun findParentLoop(ktWithLabel: KtExpressionWithLabel): IrLoop = + private fun findParentLoop(ktWithLabel: KtExpressionWithLabel): IrLoop? = findParentLoop(ktWithLabel, ktWithLabel.getLabelName()) - private fun findParentLoop(ktExpression: KtExpression, targetLabel: String?): IrLoop { + private fun findParentLoop(ktExpression: KtExpression, targetLabel: String?): IrLoop? { var finger: KtExpression? = ktExpression while (finger != null) { finger = finger.getParentOfType(true) @@ -86,7 +90,7 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen } } } - throw AssertionError("No parent loop for break/continue @$targetLabel") + return null } private fun getLoop(ktLoop: KtLoopExpression): IrLoop? { diff --git a/compiler/testData/ir/irText/expressions/badBreakContinue.kt b/compiler/testData/ir/irText/expressions/badBreakContinue.kt new file mode 100644 index 00000000000..3acd6e2f7db --- /dev/null +++ b/compiler/testData/ir/irText/expressions/badBreakContinue.kt @@ -0,0 +1,27 @@ +// !IGNORE_ERRORS + +fun test1() { + break + continue +} + +fun test2() { + L1@ while (true) { + break@ERROR + continue@ERROR + } +} + +fun test3() { + L1@ while (true) { + val lambda = { + break@L1 + continue@L1 + } + } +} + +fun test4() { + while (break) {} + while (continue) {} +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/badBreakContinue.txt b/compiler/testData/ir/irText/expressions/badBreakContinue.txt new file mode 100644 index 00000000000..2cca79ec9a1 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/badBreakContinue.txt @@ -0,0 +1,32 @@ +FILE /badBreakContinue.kt + FUN public fun test1(): kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + FUN public fun test2(): kotlin.Unit + BLOCK_BODY + WHILE label=L1 operator=WHILE_LOOP + condition: CONST Boolean type=kotlin.Boolean value='true' + body: BLOCK type=kotlin.Nothing operator=null + ERROR_EXPR 'Loop not found for break expression: break@ERROR' type=kotlin.Nothing + ERROR_EXPR 'Loop not found for continue expression: continue@ERROR' type=kotlin.Nothing + FUN public fun test3(): kotlin.Unit + BLOCK_BODY + WHILE label=L1 operator=WHILE_LOOP + condition: CONST Boolean type=kotlin.Boolean value='true' + body: BLOCK type=kotlin.Unit operator=null + VAR val lambda: () -> kotlin.Nothing + BLOCK type=() -> kotlin.Nothing operator=LAMBDA + FUN local final fun (): kotlin.Nothing + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break@L1' type=kotlin.Nothing + ERROR_EXPR 'Loop not found for continue expression: continue@L1' type=kotlin.Nothing + CALLABLE_REFERENCE '(): Nothing' type=() -> kotlin.Nothing operator=LAMBDA + FUN public fun test4(): kotlin.Unit + BLOCK_BODY + WHILE label=null operator=WHILE_LOOP + condition: ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit operator=null + WHILE label=null operator=WHILE_LOOP + condition: ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit operator=null diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 0ca3ac8ffa3..cf7c49ae70f 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -280,6 +280,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("badBreakContinue.kt") + public void testBadBreakContinue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/badBreakContinue.kt"); + doTest(fileName); + } + @TestMetadata("bangbang.kt") public void testBangbang() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/bangbang.kt");