'break' & 'continue'

This commit is contained in:
Dmitry Petrov
2016-08-24 15:32:02 +03:00
committed by Dmitry Petrov
parent 87c7b4834b
commit 03a666690b
9 changed files with 252 additions and 6 deletions
@@ -17,10 +17,12 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.expressions.IrReturnImpl
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtWhileExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import java.util.*
@@ -62,5 +64,8 @@ class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val c
fun putLoop(expression: KtExpression, irLoop: IrLoop) {
loopTable[expression] = irLoop
}
fun getLoop(expression: KtExpression): IrLoop? =
loopTable[expression]
}
@@ -17,13 +17,15 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtDoWhileExpression
import org.jetbrains.kotlin.psi.KtWhileExpression
import org.jetbrains.kotlin.psi.KtWhileExpressionBase
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
class LoopExpressionGenerator(val statementGenerator: StatementGenerator) {
class LoopExpressionGenerator(val statementGenerator: StatementGenerator) : GeneratorWithScope {
override val scope: Scope get() = statementGenerator.scope
override val context: GeneratorContext get() = statementGenerator.context
fun generateWhileExpression(expression: KtWhileExpression): IrExpression =
generateConditionalLoop(expression, IrWhileLoopImpl(expression.startOffset, expression.endOffset, IrOperator.WHILE_LOOP))
@@ -36,4 +38,47 @@ class LoopExpressionGenerator(val statementGenerator: StatementGenerator) {
irLoop.body = statementGenerator.generateExpression(expression.body!!)
return irLoop
}
fun generateBreakExpression(expression: KtBreakExpression): IrExpression {
val parentLoop = findParentLoop(expression)
return IrBreakImpl(expression.startOffset, expression.endOffset, context.builtIns.nothingType, parentLoop)
}
fun generateContinueExpression(expression: KtContinueExpression): IrExpression {
val parentLoop = findParentLoop(expression)
return IrContinueImpl(expression.startOffset, expression.endOffset, context.builtIns.nothingType, parentLoop)
}
private fun findParentLoop(expression: KtExpressionWithLabel): IrLoop =
findParentLoop(expression, expression.getTargetLabel()?.getReferencedName())
private fun findParentLoop(expression: KtExpression, targetLabel: String?): IrLoop {
var finger: KtExpression? = expression
while (finger != null) {
finger = finger.getParentOfType<KtLoopExpression>(true)
if (finger == null) {
break
}
if (targetLabel == null) {
return getLoop(finger)
}
else {
val parent = finger.parent
if (parent is KtLabeledExpression) {
val label = parent.getLabelName()!!
if (targetLabel == label) {
return getLoop(finger)
}
}
}
}
throw AssertionError("No parent loop for break/continue @$targetLabel")
}
private fun getLoop(finger: KtLoopExpression): IrLoop {
return statementGenerator.expressionBodyGenerator.getLoop(finger) ?:
throw AssertionError("Loop was not visited:\n${finger.text}")
}
}
@@ -277,6 +277,12 @@ class StatementGenerator(
override fun visitDoWhileExpression(expression: KtDoWhileExpression, data: Nothing?): IrStatement =
LoopExpressionGenerator(this).generateDoWhileExpression(expression)
override fun visitBreakExpression(expression: KtBreakExpression, data: Nothing?): IrStatement =
LoopExpressionGenerator(this).generateBreakExpression(expression)
override fun visitContinueExpression(expression: KtContinueExpression, data: Nothing?): IrStatement =
LoopExpressionGenerator(this).generateContinueExpression(expression)
}
@@ -0,0 +1,74 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrBreakContinue : IrExpression {
var loop: IrLoop
}
interface IrBreak: IrBreakContinue
interface IrContinue: IrBreakContinue
fun IrBreakContinue.getDepth(): Int {
var depth = 0
var finger: IrElement = this
while (true) {
val parent = finger.parent ?: throw AssertionError("No parent loop in tree for ${this.render()}:\n${finger.dump()}")
if (parent is IrLoop) {
if (parent == loop) {
return depth
}
depth++
}
finger = parent
}
}
abstract class IrBreakContinueBase(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override var loop: IrLoop
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrBreakContinue {
}
class IrBreakImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
loop: IrLoop
) : IrBreakContinueBase(startOffset, endOffset, type, loop), IrBreak {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitBreak(this, data)
}
class IrContinueImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
loop: IrLoop
) : IrBreakContinueBase(startOffset, endOffset, type, loop), IrContinue {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitContinue(this, data)
}
@@ -106,6 +106,12 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Nothing?): String =
"DO_WHILE operator=${loop.operator}"
override fun visitBreak(jump: IrBreak, data: Nothing?): String =
"BREAK loop.operator=${jump.loop.operator} depth=${jump.getDepth()}"
override fun visitContinue(jump: IrContinue, data: Nothing?): String =
"CONTINUE loop.operator=${jump.loop.operator} depth=${jump.getDepth()}"
override fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: Nothing?): String =
"DUMMY ${declaration.descriptor.name}"
@@ -61,8 +61,13 @@ interface IrElementVisitor<out R, in D> {
fun visitWhileLoop(loop: IrWhileLoop, data: D) = visitLoop(loop, data)
fun visitDoWhileLoop(loop: IrDoWhileLoop, data: D) = visitLoop(loop, data)
fun visitBreakContinue(jump: IrBreakContinue, data: D) = visitExpression(jump, data)
fun visitBreak(jump: IrBreak, data: D) = visitBreakContinue(jump, data)
fun visitContinue(jump: IrContinue, data: D) = visitBreakContinue(jump, data)
// NB Use it only for testing purposes; will be removed as soon as all Kotlin expression types are covered
fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: D) = visitDeclaration(declaration, data)
fun visitDummyExpression(expression: IrDummyExpression, data: D) = visitExpression(expression, data)
}
+39
View File
@@ -0,0 +1,39 @@
fun test1() {
while (true) { break }
do { break } while (true)
while (true) { continue }
do { continue } while (true)
}
fun test2() {
OUTER@while(true) {
INNER@while(true) {
break@INNER
break@OUTER
}
break@OUTER
}
OUTER@while(true) {
INNER@while(true) {
continue@INNER
continue@OUTER
}
continue@OUTER
}
}
fun test3() {
L@while(true) {
L@while(true) {
break@L
}
break@L
}
L@while(true) {
L@while(true) {
continue@L
}
continue@L
}
}
+60
View File
@@ -0,0 +1,60 @@
IrFile /breakContinue.kt
IrFunction public fun test1(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
BREAK loop.operator=WHILE_LOOP depth=0
DO_WHILE operator=DO_WHILE_LOOP
body: BLOCK type=<no-type> hasResult=false operator=null
BREAK loop.operator=DO_WHILE_LOOP depth=0
condition: CONST Boolean type=kotlin.Boolean value='true'
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
CONTINUE loop.operator=WHILE_LOOP depth=0
DO_WHILE operator=DO_WHILE_LOOP
body: BLOCK type=<no-type> hasResult=false operator=null
CONTINUE loop.operator=DO_WHILE_LOOP depth=0
condition: CONST Boolean type=kotlin.Boolean value='true'
IrFunction public fun test2(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
BREAK loop.operator=WHILE_LOOP depth=0
BREAK loop.operator=WHILE_LOOP depth=1
BREAK loop.operator=WHILE_LOOP depth=0
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
CONTINUE loop.operator=WHILE_LOOP depth=0
CONTINUE loop.operator=WHILE_LOOP depth=1
CONTINUE loop.operator=WHILE_LOOP depth=0
IrFunction public fun test3(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
BREAK loop.operator=WHILE_LOOP depth=0
BREAK loop.operator=WHILE_LOOP depth=0
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
WHILE operator=WHILE_LOOP
condition: CONST Boolean type=kotlin.Boolean value='true'
body: BLOCK type=<no-type> hasResult=false operator=null
CONTINUE loop.operator=WHILE_LOOP depth=0
CONTINUE loop.operator=WHILE_LOOP depth=0
@@ -83,6 +83,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
@TestMetadata("breakContinue.kt")
public void testBreakContinue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/breakContinue.kt");
doTest(fileName);
}
@TestMetadata("callWithReorderedArguments.kt")
public void testCallWithReorderedArguments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/callWithReorderedArguments.kt");