From e9d4de658dd3110c76d7247ea639a2d00f273aef Mon Sep 17 00:00:00 2001 From: Vladimir Sukharev Date: Wed, 20 Sep 2023 10:06:00 +0000 Subject: [PATCH] [FIR2IR] Don't emit empty body of while/do_while loop Merge-request: KT-MR-12283 Merged-by: Vladimir Sukharev --- .../kotlin/fir/backend/Fir2IrVisitor.kt | 84 ++++++++++--------- .../breakContinueInLoopHeader.fir.ir.txt | 2 - .../breakContinueInLoopHeader.fir.kt.txt | 6 +- .../expressions/whileDoWhile.fir.ir.txt | 8 +- .../expressions/whileDoWhile.fir.kt.txt | 8 +- .../ir/irText/expressions/whileDoWhile.ir.txt | 6 ++ .../ir/irText/expressions/whileDoWhile.kt | 1 + .../ir/irText/expressions/whileDoWhile.kt.txt | 4 + 8 files changed, 68 insertions(+), 51 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index 50f5b09a918..0a95cbe6ecb 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -1303,7 +1303,9 @@ class Fir2IrVisitor( ).apply { loopMap[doWhileLoop] = this label = doWhileLoop.label?.name - body = doWhileLoop.block.convertToIrExpressionOrBlock(origin) + body = runUnless(doWhileLoop.block is FirEmptyExpressionBlock) { + doWhileLoop.block.convertToIrExpressionOrBlock(origin) + } condition = convertToIrExpression(doWhileLoop.condition) loopMap.remove(doWhileLoop) } @@ -1324,49 +1326,51 @@ class Fir2IrVisitor( loopMap[whileLoop] = this label = whileLoop.label?.name condition = convertToIrExpression(whileLoop.condition) - body = if (isForLoop) { - /* - * for loops in IR must have their body in the exact following form - * because some of the lowerings (e.g. `ForLoopLowering`) expect it: - * - * for (x in list) { ...body...} - * - * IR (loop body): - * IrBlock: - * x = .next() - * ... possible destructured loop variables, in case iterator is a tuple: `for ((a,b,c) in list) { ...body...}` ... - * IrBlock: - * ...body... - */ - firLoopBody.convertWithOffsets { innerStartOffset, innerEndOffset -> - val loopBodyStatements = firLoopBody.statements - val firLoopVarStmt = loopBodyStatements.firstOrNull() - ?: error("Unexpected shape of for loop body: missing body statements") + body = runUnless(firLoopBody is FirEmptyExpressionBlock) { + if (isForLoop) { + /* + * for loops in IR must have their body in the exact following form + * because some of the lowerings (e.g. `ForLoopLowering`) expect it: + * + * for (x in list) { ...body...} + * + * IR (loop body): + * IrBlock: + * x = .next() + * ... possible destructured loop variables, in case iterator is a tuple: `for ((a,b,c) in list) { ...body...}` ... + * IrBlock: + * ...body... + */ + firLoopBody.convertWithOffsets { innerStartOffset, innerEndOffset -> + val loopBodyStatements = firLoopBody.statements + val firLoopVarStmt = loopBodyStatements.firstOrNull() + ?: error("Unexpected shape of for loop body: missing body statements") - val (destructuredLoopVariables, realStatements) = loopBodyStatements.drop(1).partition { - it is FirProperty && it.initializer is FirComponentCall - } - val firExpression = realStatements.singleOrNull() as? FirExpression - ?: error("Unexpected shape of for loop body: must be single real loop statement, but got ${realStatements.size}") - - val irStatements = buildList { - addIfNotNull(firLoopVarStmt.toIrStatement()) - destructuredLoopVariables.forEach { addIfNotNull(it.toIrStatement()) } - if (firExpression !is FirEmptyExpressionBlock) { - add(convertToIrExpression(firExpression)) + val (destructuredLoopVariables, realStatements) = loopBodyStatements.drop(1).partition { + it is FirProperty && it.initializer is FirComponentCall } - } + val firExpression = realStatements.singleOrNull() as? FirExpression + ?: error("Unexpected shape of for loop body: must be single real loop statement, but got ${realStatements.size}") - IrBlockImpl( - innerStartOffset, - innerEndOffset, - irBuiltIns.unitType, - origin, - irStatements, - ) + val irStatements = buildList { + addIfNotNull(firLoopVarStmt.toIrStatement()) + destructuredLoopVariables.forEach { addIfNotNull(it.toIrStatement()) } + if (firExpression !is FirEmptyExpressionBlock) { + add(convertToIrExpression(firExpression)) + } + } + + IrBlockImpl( + innerStartOffset, + innerEndOffset, + irBuiltIns.unitType, + origin, + irStatements, + ) + } + } else { + firLoopBody.convertToIrExpressionOrBlock(origin) } - } else { - firLoopBody.convertToIrExpressionOrBlock(origin) } loopMap.remove(whileLoop) } diff --git a/compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.fir.ir.txt b/compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.fir.ir.txt index 2ad7074b260..0472dbeec7f 100644 --- a/compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.fir.ir.txt +++ b/compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.fir.ir.txt @@ -18,7 +18,6 @@ FILE fqName: fileName:/breakContinueInLoopHeader.kt BRANCH if: CONST Boolean type=kotlin.Boolean value=true then: GET_VAR 'val tmp_0: kotlin.Boolean? declared in .test1' type=kotlin.Boolean? origin=null - body: BLOCK type=kotlin.Unit origin=null FUN name:test2 visibility:public modality:FINAL <> (c:kotlin.Boolean?) returnType:kotlin.Unit VALUE_PARAMETER name:c index:0 type:kotlin.Boolean? BLOCK_BODY @@ -38,7 +37,6 @@ FILE fqName: fileName:/breakContinueInLoopHeader.kt BRANCH if: CONST Boolean type=kotlin.Boolean value=true then: GET_VAR 'val tmp_1: kotlin.Boolean? declared in .test2' type=kotlin.Boolean? origin=null - body: BLOCK type=kotlin.Unit origin=null FUN name:test3 visibility:public modality:FINAL <> (ss:kotlin.collections.List?) returnType:kotlin.Unit VALUE_PARAMETER name:ss index:0 type:kotlin.collections.List? BLOCK_BODY diff --git a/compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.fir.kt.txt b/compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.fir.kt.txt index 8ef7c7157d0..712bb2b23be 100644 --- a/compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.fir.kt.txt @@ -6,8 +6,7 @@ fun test1(c: Boolean?) { EQEQ(arg0 = , arg1 = null) -> break@L else -> } - }) { // BLOCK - } + }) } } @@ -19,8 +18,7 @@ fun test2(c: Boolean?) { EQEQ(arg0 = , arg1 = null) -> continue@L else -> } - }) { // BLOCK - } + }) } } diff --git a/compiler/testData/ir/irText/expressions/whileDoWhile.fir.ir.txt b/compiler/testData/ir/irText/expressions/whileDoWhile.fir.ir.txt index 011e6184aee..b52ce387718 100644 --- a/compiler/testData/ir/irText/expressions/whileDoWhile.fir.ir.txt +++ b/compiler/testData/ir/irText/expressions/whileDoWhile.fir.ir.txt @@ -7,7 +7,6 @@ FILE fqName: fileName:/whileDoWhile.kt condition: CALL 'public final fun less (arg0: kotlin.Int, arg1: kotlin.Int): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=LT arg0: GET_VAR 'var x: kotlin.Int declared in .test' type=kotlin.Int origin=null arg1: CONST Int type=kotlin.Int value=0 - body: BLOCK type=kotlin.Unit origin=null WHILE label=null origin=WHILE_LOOP condition: CALL 'public final fun less (arg0: kotlin.Int, arg1: kotlin.Int): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=LT arg0: GET_VAR 'var x: kotlin.Int declared in .test' type=kotlin.Int origin=null @@ -33,10 +32,15 @@ FILE fqName: fileName:/whileDoWhile.kt GET_VAR 'val tmp_1: kotlin.Int declared in .test' type=kotlin.Int origin=null BLOCK type=kotlin.Unit origin=null DO_WHILE label=null origin=DO_WHILE_LOOP - body: COMPOSITE type=kotlin.Unit origin=null condition: CALL 'public final fun less (arg0: kotlin.Int, arg1: kotlin.Int): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=LT arg0: GET_VAR 'var x: kotlin.Int declared in .test' type=kotlin.Int origin=null arg1: CONST Int type=kotlin.Int value=0 + BLOCK type=kotlin.Unit origin=null + DO_WHILE label=null origin=DO_WHILE_LOOP + body: COMPOSITE type=kotlin.Unit origin=null + condition: CALL 'public final fun less (arg0: kotlin.Int, arg1: kotlin.Int): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=LT + arg0: GET_VAR 'var x: kotlin.Int declared in .test' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=7 BLOCK type=kotlin.Unit origin=null DO_WHILE label=null origin=DO_WHILE_LOOP body: BLOCK type=kotlin.Int origin=POSTFIX_INCR diff --git a/compiler/testData/ir/irText/expressions/whileDoWhile.fir.kt.txt b/compiler/testData/ir/irText/expressions/whileDoWhile.fir.kt.txt index f03923ad4d4..bb6180a2313 100644 --- a/compiler/testData/ir/irText/expressions/whileDoWhile.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/whileDoWhile.fir.kt.txt @@ -1,7 +1,6 @@ fun test() { var x: Int = 0 - while (less(arg0 = x, arg1 = 0)) { // BLOCK - } + while (less(arg0 = x, arg1 = 0)) while (less(arg0 = x, arg1 = 5)) { // BLOCK val : Int = x x = .inc() @@ -14,9 +13,12 @@ fun test() { } } + { // BLOCK + do while (less(arg0 = x, arg1 = 0)) + } { // BLOCK do// COMPOSITE { - // } while (less(arg0 = x, arg1 = 0)) + // } while (less(arg0 = x, arg1 = 7)) } { // BLOCK do{ // BLOCK diff --git a/compiler/testData/ir/irText/expressions/whileDoWhile.ir.txt b/compiler/testData/ir/irText/expressions/whileDoWhile.ir.txt index d0e0be57268..2fc124992c2 100644 --- a/compiler/testData/ir/irText/expressions/whileDoWhile.ir.txt +++ b/compiler/testData/ir/irText/expressions/whileDoWhile.ir.txt @@ -37,6 +37,12 @@ FILE fqName: fileName:/whileDoWhile.kt condition: CALL 'public final fun less (arg0: kotlin.Int, arg1: kotlin.Int): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=LT arg0: GET_VAR 'var x: kotlin.Int declared in .test' type=kotlin.Int origin=null arg1: CONST Int type=kotlin.Int value=0 + BLOCK type=kotlin.Unit origin=null + DO_WHILE label=null origin=DO_WHILE_LOOP + body: COMPOSITE type=kotlin.Unit origin=null + condition: CALL 'public final fun less (arg0: kotlin.Int, arg1: kotlin.Int): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=LT + arg0: GET_VAR 'var x: kotlin.Int declared in .test' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=7 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 diff --git a/compiler/testData/ir/irText/expressions/whileDoWhile.kt b/compiler/testData/ir/irText/expressions/whileDoWhile.kt index 58227d46da4..6174f0de8b7 100644 --- a/compiler/testData/ir/irText/expressions/whileDoWhile.kt +++ b/compiler/testData/ir/irText/expressions/whileDoWhile.kt @@ -4,6 +4,7 @@ fun test() { while (x < 5) x++ while (x < 10) { x++ } do while (x < 0) + do {} while (x < 7) do x++ while (x < 15) do { x ++ } while (x < 20) } diff --git a/compiler/testData/ir/irText/expressions/whileDoWhile.kt.txt b/compiler/testData/ir/irText/expressions/whileDoWhile.kt.txt index 7bcfcf4c881..cc3499e2efe 100644 --- a/compiler/testData/ir/irText/expressions/whileDoWhile.kt.txt +++ b/compiler/testData/ir/irText/expressions/whileDoWhile.kt.txt @@ -16,6 +16,10 @@ fun test() { { // BLOCK do while (less(arg0 = x, arg1 = 0)) } + { // BLOCK + do// COMPOSITE { + // } while (less(arg0 = x, arg1 = 7)) + } { // BLOCK do{ // BLOCK val tmp2: Int = x