From d096f1d381920171798ba5195a11c74d716bfc9d Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 6 Mar 2017 19:02:52 +0300 Subject: [PATCH] 'while' and 'do-while' loop generator fixes. Generate 'do-while' loop body as IrComposite, because variables declared in loop body should be visible in loop condition. Wrap 'do-while' loop in IrBlock so that variables declared in loop body are not visible outside of the loop. Generate 'while' and 'do-while' loops as expressions of type Unit. --- .../ir2cfg/generators/FunctionGenerator.kt | 2 +- .../generators/LoopExpressionGenerator.kt | 35 ++++++++- .../irText/expressions/badBreakContinue.txt | 2 +- .../ir/irText/expressions/breakContinue.txt | 34 ++++----- .../ir/irText/expressions/whileDoWhile.txt | 72 ++++++++++--------- .../ir/irText/regressions/coercionInLoop.txt | 30 ++++---- 6 files changed, 105 insertions(+), 70 deletions(-) diff --git a/compiler/ir/ir.ir2cfg/src/org/jetbrains/kotlin/ir2cfg/generators/FunctionGenerator.kt b/compiler/ir/ir.ir2cfg/src/org/jetbrains/kotlin/ir2cfg/generators/FunctionGenerator.kt index 910fb49b631..3c36219b4b8 100644 --- a/compiler/ir/ir.ir2cfg/src/org/jetbrains/kotlin/ir2cfg/generators/FunctionGenerator.kt +++ b/compiler/ir/ir.ir2cfg/src/org/jetbrains/kotlin/ir2cfg/generators/FunctionGenerator.kt @@ -72,7 +72,7 @@ class FunctionGenerator(val function: IrFunction) { return body.process() } - override fun visitBlock(expression: IrBlock, data: Boolean): IrStatement? { + override fun visitContainerExpression(expression: IrContainerExpression, data: Boolean): IrStatement? { return expression.process() ?: expression } 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 119b9fadfa1..b40d77088b3 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 @@ -39,14 +39,43 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen IrDoWhileLoopImpl(ktDoWhile.startOffset, ktDoWhile.endOffset, context.builtIns.unitType, IrStatementOrigin.DO_WHILE_LOOP)) - private fun generateConditionalLoop(ktLoop: KtWhileExpressionBase, irLoop: IrLoopBase): IrLoop { + private fun generateConditionalLoop(ktLoop: KtWhileExpressionBase, irLoop: IrLoopBase): IrExpression { irLoop.condition = statementGenerator.generateExpression(ktLoop.condition!!) statementGenerator.bodyGenerator.putLoop(ktLoop, irLoop) - irLoop.body = ktLoop.body?.let { statementGenerator.generateExpression(ktLoop.body!!) } + irLoop.body = ktLoop.body?.let { ktLoopBody -> + if (ktLoopBody is KtBlockExpression) { + if (ktLoop is KtDoWhileExpression) + generateDoWhileLoopBody(ktLoopBody) + else + generateWhileLoopBody(ktLoopBody) + } + else + statementGenerator.generateExpression(ktLoopBody) + } irLoop.label = getLoopLabel(ktLoop) - return irLoop + + return if (ktLoop is KtDoWhileExpression) { + IrBlockImpl(ktLoop.startOffset, ktLoop.endOffset, context.builtIns.unitType).apply { + statements.add(irLoop) + } + } + else + irLoop } + private fun generateWhileLoopBody(ktLoopBody: KtBlockExpression): IrExpression = + IrBlockImpl( + ktLoopBody.startOffset, ktLoopBody.endOffset, context.builtIns.unitType, null, + ktLoopBody.statements.map { statementGenerator.generateStatement(it) } + ) + + + private fun generateDoWhileLoopBody(ktLoopBody: KtBlockExpression): IrExpression = + IrCompositeImpl( + ktLoopBody.startOffset, ktLoopBody.endOffset, context.builtIns.unitType, null, + ktLoopBody.statements.map { statementGenerator.generateStatement(it) } + ) + fun generateBreak(ktBreak: KtBreakExpression): IrExpression { val parentLoop = findParentLoop(ktBreak) ?: return ErrorExpressionGenerator(statementGenerator).generateErrorExpression( diff --git a/compiler/testData/ir/irText/expressions/badBreakContinue.txt b/compiler/testData/ir/irText/expressions/badBreakContinue.txt index 2a34b6696ba..6f59ae86642 100644 --- a/compiler/testData/ir/irText/expressions/badBreakContinue.txt +++ b/compiler/testData/ir/irText/expressions/badBreakContinue.txt @@ -7,7 +7,7 @@ FILE /badBreakContinue.kt BLOCK_BODY WHILE label=L1 origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=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 diff --git a/compiler/testData/ir/irText/expressions/breakContinue.txt b/compiler/testData/ir/irText/expressions/breakContinue.txt index 19f3bcbfc6c..34fa640c881 100644 --- a/compiler/testData/ir/irText/expressions/breakContinue.txt +++ b/compiler/testData/ir/irText/expressions/breakContinue.txt @@ -3,37 +3,39 @@ FILE /breakContinue.kt BLOCK_BODY WHILE label=null origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null - BREAK label=null loop.label=null - DO_WHILE label=null origin=DO_WHILE_LOOP body: BLOCK type=kotlin.Unit origin=null BREAK label=null loop.label=null - condition: CONST Boolean type=kotlin.Boolean value='true' + BLOCK type=kotlin.Unit origin=null + DO_WHILE label=null origin=DO_WHILE_LOOP + body: COMPOSITE type=kotlin.Unit origin=null + BREAK label=null loop.label=null + condition: CONST Boolean type=kotlin.Boolean value='true' WHILE label=null origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null - CONTINUE label=null loop.label=null - DO_WHILE label=null origin=DO_WHILE_LOOP body: BLOCK type=kotlin.Unit origin=null CONTINUE label=null loop.label=null - condition: CONST Boolean type=kotlin.Boolean value='true' + BLOCK type=kotlin.Unit origin=null + DO_WHILE label=null origin=DO_WHILE_LOOP + body: COMPOSITE type=kotlin.Unit origin=null + CONTINUE label=null loop.label=null + condition: CONST Boolean type=kotlin.Boolean value='true' FUN public fun test2(): kotlin.Unit BLOCK_BODY WHILE label=OUTER origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=null WHILE label=INNER origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=null BREAK label=INNER loop.label=INNER BREAK label=OUTER loop.label=OUTER BREAK label=OUTER loop.label=OUTER WHILE label=OUTER origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=null WHILE label=INNER origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=null CONTINUE label=INNER loop.label=INNER CONTINUE label=OUTER loop.label=OUTER CONTINUE label=OUTER loop.label=OUTER @@ -41,17 +43,17 @@ FILE /breakContinue.kt BLOCK_BODY WHILE label=L origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=null WHILE label=L origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=null BREAK label=L loop.label=L BREAK label=L loop.label=L WHILE label=L origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=null WHILE label=L origin=WHILE_LOOP condition: CONST Boolean type=kotlin.Boolean value='true' - body: BLOCK type=kotlin.Nothing origin=null + body: BLOCK type=kotlin.Unit origin=null CONTINUE label=L loop.label=L CONTINUE label=L loop.label=L diff --git a/compiler/testData/ir/irText/expressions/whileDoWhile.txt b/compiler/testData/ir/irText/expressions/whileDoWhile.txt index f518f41c73f..d1f4a8bf6c5 100644 --- a/compiler/testData/ir/irText/expressions/whileDoWhile.txt +++ b/compiler/testData/ir/irText/expressions/whileDoWhile.txt @@ -26,8 +26,8 @@ FILE /whileDoWhile.kt arg0: CALL 'compareTo(Int): Int' type=kotlin.Int origin=LT $this: GET_VAR 'x: Int' type=kotlin.Int origin=null other: CONST Int type=kotlin.Int value='10' - body: TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit - BLOCK type=kotlin.Int origin=null + body: BLOCK type=kotlin.Unit origin=null + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit BLOCK type=kotlin.Int origin=POSTFIX_INCR VAR IR_TEMPORARY_VARIABLE val tmp1: kotlin.Int GET_VAR 'x: Int' type=kotlin.Int origin=POSTFIX_INCR @@ -35,38 +35,41 @@ FILE /whileDoWhile.kt CALL 'inc(): Int' type=kotlin.Int origin=POSTFIX_INCR $this: GET_VAR 'tmp1: Int' type=kotlin.Int origin=null GET_VAR 'tmp1: Int' type=kotlin.Int origin=null - DO_WHILE label=null origin=DO_WHILE_LOOP - condition: CALL 'LT0(Int): Boolean' type=kotlin.Boolean origin=LT - arg0: CALL 'compareTo(Int): Int' type=kotlin.Int origin=LT - $this: GET_VAR 'x: Int' type=kotlin.Int origin=null - other: CONST Int type=kotlin.Int value='0' - DO_WHILE label=null origin=DO_WHILE_LOOP - body: TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit - BLOCK type=kotlin.Int origin=POSTFIX_INCR - VAR IR_TEMPORARY_VARIABLE val tmp2: kotlin.Int - GET_VAR 'x: Int' type=kotlin.Int origin=POSTFIX_INCR - SET_VAR 'x: Int' type=kotlin.Unit origin=POSTFIX_INCR - CALL 'inc(): Int' type=kotlin.Int origin=POSTFIX_INCR - $this: GET_VAR 'tmp2: Int' type=kotlin.Int origin=null - GET_VAR 'tmp2: Int' type=kotlin.Int origin=null - condition: CALL 'LT0(Int): Boolean' type=kotlin.Boolean origin=LT - arg0: CALL 'compareTo(Int): Int' type=kotlin.Int origin=LT - $this: GET_VAR 'x: Int' type=kotlin.Int origin=null - other: CONST Int type=kotlin.Int value='15' - DO_WHILE label=null origin=DO_WHILE_LOOP - body: BLOCK type=kotlin.Unit origin=null - TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit + BLOCK type=kotlin.Unit origin=null + DO_WHILE label=null origin=DO_WHILE_LOOP + condition: CALL 'LT0(Int): Boolean' type=kotlin.Boolean origin=LT + arg0: CALL 'compareTo(Int): Int' type=kotlin.Int origin=LT + $this: GET_VAR 'x: Int' type=kotlin.Int origin=null + other: CONST Int type=kotlin.Int value='0' + BLOCK type=kotlin.Unit origin=null + DO_WHILE label=null origin=DO_WHILE_LOOP + body: TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit BLOCK type=kotlin.Int origin=POSTFIX_INCR - VAR IR_TEMPORARY_VARIABLE val tmp3: kotlin.Int + VAR IR_TEMPORARY_VARIABLE val tmp2: kotlin.Int GET_VAR 'x: Int' type=kotlin.Int origin=POSTFIX_INCR SET_VAR 'x: Int' type=kotlin.Unit origin=POSTFIX_INCR CALL 'inc(): Int' type=kotlin.Int origin=POSTFIX_INCR - $this: GET_VAR 'tmp3: Int' type=kotlin.Int origin=null - GET_VAR 'tmp3: Int' type=kotlin.Int origin=null - condition: CALL 'LT0(Int): Boolean' type=kotlin.Boolean origin=LT - arg0: CALL 'compareTo(Int): Int' type=kotlin.Int origin=LT - $this: GET_VAR 'x: Int' type=kotlin.Int origin=null - other: CONST Int type=kotlin.Int value='20' + $this: GET_VAR 'tmp2: Int' type=kotlin.Int origin=null + GET_VAR 'tmp2: Int' type=kotlin.Int origin=null + condition: CALL 'LT0(Int): Boolean' type=kotlin.Boolean origin=LT + arg0: CALL 'compareTo(Int): Int' type=kotlin.Int origin=LT + $this: GET_VAR 'x: Int' type=kotlin.Int origin=null + other: CONST Int type=kotlin.Int value='15' + BLOCK type=kotlin.Unit origin=null + DO_WHILE label=null origin=DO_WHILE_LOOP + body: COMPOSITE type=kotlin.Unit origin=null + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit + BLOCK type=kotlin.Int origin=POSTFIX_INCR + VAR IR_TEMPORARY_VARIABLE val tmp3: kotlin.Int + GET_VAR 'x: Int' type=kotlin.Int origin=POSTFIX_INCR + SET_VAR 'x: Int' type=kotlin.Unit origin=POSTFIX_INCR + CALL 'inc(): Int' type=kotlin.Int origin=POSTFIX_INCR + $this: GET_VAR 'tmp3: Int' type=kotlin.Int origin=null + GET_VAR 'tmp3: Int' type=kotlin.Int origin=null + condition: CALL 'LT0(Int): Boolean' type=kotlin.Boolean origin=LT + arg0: CALL 'compareTo(Int): Int' type=kotlin.Int origin=LT + $this: GET_VAR 'x: Int' type=kotlin.Int origin=null + other: CONST Int type=kotlin.Int value='20' FUN public fun testSmartcastInCondition(): kotlin.Unit BLOCK_BODY VAR val a: kotlin.Any? = null @@ -80,7 +83,8 @@ FILE /whileDoWhile.kt condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean GET_VAR 'a: Any?' type=kotlin.Any? origin=null body: BLOCK type=kotlin.Unit origin=null - DO_WHILE label=null origin=DO_WHILE_LOOP - body: BLOCK type=kotlin.Unit origin=null - condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean - GET_VAR 'a: Any?' type=kotlin.Any? origin=null + BLOCK type=kotlin.Unit origin=null + DO_WHILE label=null origin=DO_WHILE_LOOP + body: COMPOSITE type=kotlin.Unit origin=null + condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean + GET_VAR 'a: Any?' type=kotlin.Any? origin=null diff --git a/compiler/testData/ir/irText/regressions/coercionInLoop.txt b/compiler/testData/ir/irText/regressions/coercionInLoop.txt index 6ab5c305d00..93d7d62db63 100644 --- a/compiler/testData/ir/irText/regressions/coercionInLoop.txt +++ b/compiler/testData/ir/irText/regressions/coercionInLoop.txt @@ -12,21 +12,21 @@ FILE /coercionInLoop.kt WHILE label=null origin=WHILE_LOOP condition: CALL 'hasNext(): Boolean' type=kotlin.Boolean origin=null $this: GET_VAR 'x: DoubleIterator' type=kotlin.collections.DoubleIterator origin=null - body: TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit - BLOCK type=kotlin.Int origin=null - WHEN type=kotlin.Unit origin=null - BRANCH - if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ - arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ - arg0: CALL 'get(Int): Double' type=kotlin.Double origin=GET_ARRAY_ELEMENT - $this: GET_VAR 'a: DoubleArray' type=kotlin.DoubleArray origin=null - index: GET_VAR 'i: Int' type=kotlin.Int origin=null - arg1: CALL 'next(): Double' type=kotlin.Double origin=null - $this: GET_VAR 'x: DoubleIterator' type=kotlin.collections.DoubleIterator origin=null - then: RETURN type=kotlin.Nothing from='box(): String' - STRING_CONCATENATION type=kotlin.String - CONST String type=kotlin.String value='Fail ' - GET_VAR 'i: Int' type=kotlin.Int origin=null + body: BLOCK type=kotlin.Unit origin=null + WHEN type=kotlin.Unit origin=null + BRANCH + if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ + arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ + arg0: CALL 'get(Int): Double' type=kotlin.Double origin=GET_ARRAY_ELEMENT + $this: GET_VAR 'a: DoubleArray' type=kotlin.DoubleArray origin=null + index: GET_VAR 'i: Int' type=kotlin.Int origin=null + arg1: CALL 'next(): Double' type=kotlin.Double origin=null + $this: GET_VAR 'x: DoubleIterator' type=kotlin.collections.DoubleIterator origin=null + then: RETURN type=kotlin.Nothing from='box(): String' + STRING_CONCATENATION type=kotlin.String + CONST String type=kotlin.String value='Fail ' + GET_VAR 'i: Int' type=kotlin.Int origin=null + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit BLOCK type=kotlin.Int origin=POSTFIX_INCR VAR IR_TEMPORARY_VARIABLE val tmp0: kotlin.Int GET_VAR 'i: Int' type=kotlin.Int origin=POSTFIX_INCR