'break' & 'continue'
This commit is contained in:
committed by
Dmitry Petrov
parent
87c7b4834b
commit
03a666690b
+7
-2
@@ -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]
|
||||
}
|
||||
|
||||
|
||||
+49
-4
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user