FIR: fix label stealling and crash with multiple labels
Consider the code below
```
fun test() {
a@ b@ {
{}
}
}
```
Currently when the code is converted to FIR, label `b` is bound to the
outer lambda and `a` gets bound to the inner lambda because it's not
consumed. This is wrong and also leads transfromation to fail with
exceptions because of the unexpected consumption of `a`.
This change fixes the above issue by designating a specific node in the
AST as the allowed user of a label when the label is added.
This commit is contained in:
committed by
TeamCityServer
parent
5c716ea979
commit
fadde98a86
+6
@@ -6271,6 +6271,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nestedLoopsWithMultipleLabels.kt")
|
||||||
|
public void testNestedLoopsWithMultipleLabels() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/controlStructures/nestedLoopsWithMultipleLabels.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("notAFunctionLabel_after.kt")
|
@TestMetadata("notAFunctionLabel_after.kt")
|
||||||
public void testNotAFunctionLabel_after() throws Exception {
|
public void testNotAFunctionLabel_after() throws Exception {
|
||||||
|
|||||||
+6
@@ -6271,6 +6271,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nestedLoopsWithMultipleLabels.kt")
|
||||||
|
public void testNestedLoopsWithMultipleLabels() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/controlStructures/nestedLoopsWithMultipleLabels.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("notAFunctionLabel_after.kt")
|
@TestMetadata("notAFunctionLabel_after.kt")
|
||||||
public void testNotAFunctionLabel_after() throws Exception {
|
public void testNotAFunctionLabel_after() throws Exception {
|
||||||
|
|||||||
+6
@@ -6271,6 +6271,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nestedLoopsWithMultipleLabels.kt")
|
||||||
|
public void testNestedLoopsWithMultipleLabels() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/controlStructures/nestedLoopsWithMultipleLabels.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("notAFunctionLabel_after.kt")
|
@TestMetadata("notAFunctionLabel_after.kt")
|
||||||
public void testNotAFunctionLabel_after() throws Exception {
|
public void testNotAFunctionLabel_after() throws Exception {
|
||||||
|
|||||||
+18
-12
@@ -67,6 +67,8 @@ class ExpressionsConverter(
|
|||||||
return when (expression.tokenType) {
|
return when (expression.tokenType) {
|
||||||
LAMBDA_EXPRESSION -> {
|
LAMBDA_EXPRESSION -> {
|
||||||
val lambdaTree = LightTree2Fir.buildLightTreeLambdaExpression(expression.asText)
|
val lambdaTree = LightTree2Fir.buildLightTreeLambdaExpression(expression.asText)
|
||||||
|
// Pass on label user to the lambda root
|
||||||
|
context.forwardLabelUsagePermission(expression, lambdaTree.root)
|
||||||
declarationsConverter.withOffset(offset + expression.startOffset) {
|
declarationsConverter.withOffset(offset + expression.startOffset) {
|
||||||
ExpressionsConverter(baseSession, lambdaTree, declarationsConverter, context)
|
ExpressionsConverter(baseSession, lambdaTree, declarationsConverter, context)
|
||||||
.convertLambdaExpression(lambdaTree.root)
|
.convertLambdaExpression(lambdaTree.root)
|
||||||
@@ -100,7 +102,11 @@ class ExpressionsConverter(
|
|||||||
BREAK, CONTINUE -> convertLoopJump(expression)
|
BREAK, CONTINUE -> convertLoopJump(expression)
|
||||||
RETURN -> convertReturn(expression)
|
RETURN -> convertReturn(expression)
|
||||||
THROW -> convertThrow(expression)
|
THROW -> convertThrow(expression)
|
||||||
PARENTHESIZED -> getAsFirExpression(expression.getExpressionInParentheses(), "Empty parentheses")
|
PARENTHESIZED -> {
|
||||||
|
val content = expression.getExpressionInParentheses()
|
||||||
|
context.forwardLabelUsagePermission(expression, content)
|
||||||
|
getAsFirExpression(content, "Empty parentheses")
|
||||||
|
}
|
||||||
PROPERTY_DELEGATE, INDICES, CONDITION, LOOP_RANGE ->
|
PROPERTY_DELEGATE, INDICES, CONDITION, LOOP_RANGE ->
|
||||||
getAsFirExpression(expression.getExpressionInParentheses(), errorReason)
|
getAsFirExpression(expression.getExpressionInParentheses(), errorReason)
|
||||||
THIS_EXPRESSION -> convertThisExpression(expression)
|
THIS_EXPRESSION -> convertThisExpression(expression)
|
||||||
@@ -136,7 +142,7 @@ class ExpressionsConverter(
|
|||||||
receiverTypeRef = implicitType
|
receiverTypeRef = implicitType
|
||||||
symbol = FirAnonymousFunctionSymbol()
|
symbol = FirAnonymousFunctionSymbol()
|
||||||
isLambda = true
|
isLambda = true
|
||||||
label = context.firLabels.pop() ?: context.calleeNamesForLambda.lastOrNull()?.let {
|
label = context.getLastLabel(lambdaExpression) ?: context.calleeNamesForLambda.lastOrNull()?.let {
|
||||||
buildLabel {
|
buildLabel {
|
||||||
source = expressionSource.fakeElement(FirFakeSourceElementKind.GeneratedLambdaLabel)
|
source = expressionSource.fakeElement(FirFakeSourceElementKind.GeneratedLambdaLabel)
|
||||||
name = it.asString()
|
name = it.asString()
|
||||||
@@ -326,15 +332,15 @@ class ExpressionsConverter(
|
|||||||
*/
|
*/
|
||||||
private fun convertLabeledExpression(labeledExpression: LighterASTNode): FirElement {
|
private fun convertLabeledExpression(labeledExpression: LighterASTNode): FirElement {
|
||||||
var firExpression: FirElement? = null
|
var firExpression: FirElement? = null
|
||||||
val previousLabelsSize = context.firLabels.size
|
|
||||||
var errorLabelSource: FirSourceElement? = null
|
var errorLabelSource: FirSourceElement? = null
|
||||||
|
|
||||||
labeledExpression.forEachChildren {
|
labeledExpression.forEachChildren {
|
||||||
|
context.setNewLabelUserNode(it)
|
||||||
when (it.tokenType) {
|
when (it.tokenType) {
|
||||||
LABEL_QUALIFIER -> {
|
LABEL_QUALIFIER -> {
|
||||||
val rawName = it.toString()
|
val rawName = it.toString()
|
||||||
val pair = buildLabelAndErrorSource(rawName.substring(0, rawName.length - 1), it.toFirSourceElement())
|
val pair = buildLabelAndErrorSource(rawName.substring(0, rawName.length - 1), it.toFirSourceElement())
|
||||||
context.firLabels += pair.first
|
context.addNewLabel(pair.first)
|
||||||
errorLabelSource = pair.second
|
errorLabelSource = pair.second
|
||||||
}
|
}
|
||||||
BLOCK -> firExpression = declarationsConverter.convertBlock(it)
|
BLOCK -> firExpression = declarationsConverter.convertBlock(it)
|
||||||
@@ -343,9 +349,7 @@ class ExpressionsConverter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.firLabels.size != previousLabelsSize) {
|
context.dropLastLabel()
|
||||||
context.firLabels.removeLast()
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildExpressionWithErrorLabel(firExpression, errorLabelSource, labeledExpression.toFirSourceElement())
|
return buildExpressionWithErrorLabel(firExpression, errorLabelSource, labeledExpression.toFirSourceElement())
|
||||||
}
|
}
|
||||||
@@ -426,7 +430,10 @@ class ExpressionsConverter(
|
|||||||
when (it.tokenType) {
|
when (it.tokenType) {
|
||||||
ANNOTATION -> firAnnotationList += declarationsConverter.convertAnnotation(it)
|
ANNOTATION -> firAnnotationList += declarationsConverter.convertAnnotation(it)
|
||||||
ANNOTATION_ENTRY -> firAnnotationList += declarationsConverter.convertAnnotationEntry(it)
|
ANNOTATION_ENTRY -> firAnnotationList += declarationsConverter.convertAnnotationEntry(it)
|
||||||
else -> if (it.isExpression()) firExpression = getAsFirExpression(it)
|
else -> if (it.isExpression()) {
|
||||||
|
context.forwardLabelUsagePermission(annotatedExpression, it)
|
||||||
|
firExpression = getAsFirExpression(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,7 +1005,7 @@ class ExpressionsConverter(
|
|||||||
return FirDoWhileLoopBuilder().apply {
|
return FirDoWhileLoopBuilder().apply {
|
||||||
source = doWhileLoop.toFirSourceElement()
|
source = doWhileLoop.toFirSourceElement()
|
||||||
// For break/continue in the do-while loop condition, prepare the loop target first so that it can refer to the same loop.
|
// For break/continue in the do-while loop condition, prepare the loop target first so that it can refer to the same loop.
|
||||||
target = prepareTarget()
|
target = prepareTarget(doWhileLoop)
|
||||||
doWhileLoop.forEachChildren {
|
doWhileLoop.forEachChildren {
|
||||||
when (it.tokenType) {
|
when (it.tokenType) {
|
||||||
BODY -> block = it
|
BODY -> block = it
|
||||||
@@ -1017,7 +1024,6 @@ class ExpressionsConverter(
|
|||||||
private fun convertWhile(whileLoop: LighterASTNode): FirElement {
|
private fun convertWhile(whileLoop: LighterASTNode): FirElement {
|
||||||
var block: LighterASTNode? = null
|
var block: LighterASTNode? = null
|
||||||
var firCondition: FirExpression? = null
|
var firCondition: FirExpression? = null
|
||||||
val label = stashLabel() //get label of while, otherwise if condition has lambda, it will steal the label
|
|
||||||
whileLoop.forEachChildren {
|
whileLoop.forEachChildren {
|
||||||
when (it.tokenType) {
|
when (it.tokenType) {
|
||||||
BODY -> block = it
|
BODY -> block = it
|
||||||
@@ -1032,7 +1038,7 @@ class ExpressionsConverter(
|
|||||||
firCondition ?: buildErrorExpression(null, ConeSimpleDiagnostic("No condition in while loop", DiagnosticKind.Syntax))
|
firCondition ?: buildErrorExpression(null, ConeSimpleDiagnostic("No condition in while loop", DiagnosticKind.Syntax))
|
||||||
// break/continue in the while loop condition will refer to an outer loop if any.
|
// break/continue in the while loop condition will refer to an outer loop if any.
|
||||||
// So, prepare the loop target after building the condition.
|
// So, prepare the loop target after building the condition.
|
||||||
target = prepareTarget(label)
|
target = prepareTarget(whileLoop)
|
||||||
}.configure(target) { convertLoopBody(block) }
|
}.configure(target) { convertLoopBody(block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1085,7 +1091,7 @@ class ExpressionsConverter(
|
|||||||
}
|
}
|
||||||
// break/continue in the for loop condition will refer to an outer loop if any.
|
// break/continue in the for loop condition will refer to an outer loop if any.
|
||||||
// So, prepare the loop target after building the condition.
|
// So, prepare the loop target after building the condition.
|
||||||
target = prepareTarget()
|
target = prepareTarget(forLoop)
|
||||||
}.configure(target) {
|
}.configure(target) {
|
||||||
// NB: just body.toFirBlock() isn't acceptable here because we need to add some statements
|
// NB: just body.toFirBlock() isn't acceptable here because we need to add some statements
|
||||||
buildBlock block@{
|
buildBlock block@{
|
||||||
|
|||||||
+14
-15
@@ -1323,7 +1323,7 @@ open class RawFirBuilder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val expressionSource = expression.toFirSourceElement()
|
val expressionSource = expression.toFirSourceElement()
|
||||||
label = context.firLabels.pop() ?: context.calleeNamesForLambda.lastOrNull()?.let {
|
label = context.getLastLabel(expression) ?: context.calleeNamesForLambda.lastOrNull()?.let {
|
||||||
buildLabel {
|
buildLabel {
|
||||||
source = expressionSource.fakeElement(FirFakeSourceElementKind.GeneratedLambdaLabel)
|
source = expressionSource.fakeElement(FirFakeSourceElementKind.GeneratedLambdaLabel)
|
||||||
name = it.asString()
|
name = it.asString()
|
||||||
@@ -1945,7 +1945,7 @@ open class RawFirBuilder(
|
|||||||
return FirDoWhileLoopBuilder().apply {
|
return FirDoWhileLoopBuilder().apply {
|
||||||
source = expression.toFirSourceElement()
|
source = expression.toFirSourceElement()
|
||||||
// For break/continue in the do-while loop condition, prepare the loop target first so that it can refer to the same loop.
|
// For break/continue in the do-while loop condition, prepare the loop target first so that it can refer to the same loop.
|
||||||
target = prepareTarget()
|
target = prepareTarget(expression)
|
||||||
condition = expression.condition.toFirExpression("No condition in do-while loop")
|
condition = expression.condition.toFirExpression("No condition in do-while loop")
|
||||||
}.configure(target) { expression.body.toFirBlock() }
|
}.configure(target) { expression.body.toFirBlock() }
|
||||||
}
|
}
|
||||||
@@ -1954,11 +1954,10 @@ open class RawFirBuilder(
|
|||||||
val target: FirLoopTarget
|
val target: FirLoopTarget
|
||||||
return FirWhileLoopBuilder().apply {
|
return FirWhileLoopBuilder().apply {
|
||||||
source = expression.toFirSourceElement()
|
source = expression.toFirSourceElement()
|
||||||
val label = stashLabel() //get label of while, otherwise if condition has lambda, it will steal the label
|
|
||||||
condition = expression.condition.toFirExpression("No condition in while loop")
|
condition = expression.condition.toFirExpression("No condition in while loop")
|
||||||
// break/continue in the while loop condition will refer to an outer loop if any.
|
// break/continue in the while loop condition will refer to an outer loop if any.
|
||||||
// So, prepare the loop target after building the condition.
|
// So, prepare the loop target after building the condition.
|
||||||
target = prepareTarget(label)
|
target = prepareTarget(expression)
|
||||||
}.configure(target) { expression.body.toFirBlock() }
|
}.configure(target) { expression.body.toFirBlock() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1995,7 +1994,7 @@ open class RawFirBuilder(
|
|||||||
}
|
}
|
||||||
// break/continue in the for loop condition will refer to an outer loop if any.
|
// break/continue in the for loop condition will refer to an outer loop if any.
|
||||||
// So, prepare the loop target after building the condition.
|
// So, prepare the loop target after building the condition.
|
||||||
target = prepareTarget()
|
target = prepareTarget(expression)
|
||||||
}.configure(target) {
|
}.configure(target) {
|
||||||
// NB: just body.toFirBlock() isn't acceptable here because we need to add some statements
|
// NB: just body.toFirBlock() isn't acceptable here because we need to add some statements
|
||||||
val blockBuilder = when (val body = expression.body) {
|
val blockBuilder = when (val body = expression.body) {
|
||||||
@@ -2323,33 +2322,33 @@ open class RawFirBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): FirElement {
|
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): FirElement {
|
||||||
|
context.forwardLabelUsagePermission(expression, expression.expression)
|
||||||
return expression.expression?.accept(this, data)
|
return expression.expression?.accept(this, data)
|
||||||
?: buildErrorExpression(expression.toFirSourceElement(), ConeSimpleDiagnostic("Empty parentheses", DiagnosticKind.Syntax))
|
?: buildErrorExpression(expression.toFirSourceElement(), ConeSimpleDiagnostic("Empty parentheses", DiagnosticKind.Syntax))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitLabeledExpression(expression: KtLabeledExpression, data: Unit): FirElement {
|
override fun visitLabeledExpression(expression: KtLabeledExpression, data: Unit): FirElement {
|
||||||
val label = expression.getTargetLabel()
|
val label = expression.getTargetLabel()
|
||||||
val previousLabelsSize = context.firLabels.size
|
|
||||||
var errorLabelSource: FirSourceElement? = null
|
var errorLabelSource: FirSourceElement? = null
|
||||||
|
|
||||||
if (label != null) {
|
val result = if (label != null) {
|
||||||
val rawName = label.getReferencedNameElement().node!!.text
|
val rawName = label.getReferencedNameElement().node!!.text
|
||||||
val labelAndErrorSource = buildLabelAndErrorSource(rawName, label.toFirPsiSourceElement())
|
val labelAndErrorSource = buildLabelAndErrorSource(rawName, label.toFirPsiSourceElement())
|
||||||
context.firLabels += labelAndErrorSource.first
|
|
||||||
errorLabelSource = labelAndErrorSource.second
|
errorLabelSource = labelAndErrorSource.second
|
||||||
}
|
context.withNewLabel(labelAndErrorSource.first, expression.baseExpression) {
|
||||||
|
expression.baseExpression?.accept(this, data)
|
||||||
val result = expression.baseExpression?.accept(this, data)
|
}
|
||||||
|
} else {
|
||||||
if (context.firLabels.size != previousLabelsSize) {
|
expression.baseExpression?.accept(this, data)
|
||||||
context.firLabels.removeLast()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildExpressionWithErrorLabel(result, errorLabelSource, expression.toFirSourceElement())
|
return buildExpressionWithErrorLabel(result, errorLabelSource, expression.toFirSourceElement())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitAnnotatedExpression(expression: KtAnnotatedExpression, data: Unit): FirElement {
|
override fun visitAnnotatedExpression(expression: KtAnnotatedExpression, data: Unit): FirElement {
|
||||||
val rawResult = expression.baseExpression?.accept(this, data)
|
val baseExpression = expression.baseExpression
|
||||||
|
context.forwardLabelUsagePermission(expression, baseExpression)
|
||||||
|
val rawResult = baseExpression?.accept(this, data)
|
||||||
val result = rawResult as? FirAnnotationContainer
|
val result = rawResult as? FirAnnotationContainer
|
||||||
?: buildErrorExpression(
|
?: buildErrorExpression(
|
||||||
expression.toFirSourceElement(),
|
expression.toFirSourceElement(),
|
||||||
|
|||||||
+2
-4
@@ -183,7 +183,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
target = FirFunctionTarget(labelName, false).apply {
|
target = FirFunctionTarget(labelName, false).apply {
|
||||||
if (context.firLabels.any { it.name == labelName } || context.firLoopTargets.any { it.labelName == labelName }) {
|
if (context.firLabels.any { it.name == labelName }) {
|
||||||
bindToErrorFunction("Label $labelName does not target a function", DiagnosticKind.NotAFunctionLabel)
|
bindToErrorFunction("Label $labelName does not target a function", DiagnosticKind.NotAFunctionLabel)
|
||||||
} else {
|
} else {
|
||||||
bindToErrorFunction("Cannot bind label $labelName to a function", DiagnosticKind.UnresolvedLabel)
|
bindToErrorFunction("Cannot bind label $labelName to a function", DiagnosticKind.UnresolvedLabel)
|
||||||
@@ -217,9 +217,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FirLoopBuilder.prepareTarget(): FirLoopTarget = prepareTarget(context.firLabels.pop())
|
fun FirLoopBuilder.prepareTarget(firLabelUser: Any): FirLoopTarget = prepareTarget(context.getLastLabel(firLabelUser))
|
||||||
|
|
||||||
fun stashLabel(): FirLabel? = context.firLabels.pop()
|
|
||||||
|
|
||||||
fun FirLoopBuilder.prepareTarget(label: FirLabel?): FirLoopTarget {
|
fun FirLoopBuilder.prepareTarget(label: FirLabel?): FirLoopTarget {
|
||||||
this.label = label
|
this.label = label
|
||||||
|
|||||||
+59
-5
@@ -5,10 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.builder
|
package org.jetbrains.kotlin.fir.builder
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirFunctionTarget
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.FirLabel
|
|
||||||
import org.jetbrains.kotlin.fir.FirLoopTarget
|
|
||||||
import org.jetbrains.kotlin.fir.FirSourceElementKind
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
|
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||||
@@ -26,7 +23,19 @@ class Context<T> {
|
|||||||
|
|
||||||
val firFunctionTargets = mutableListOf<FirFunctionTarget>()
|
val firFunctionTargets = mutableListOf<FirFunctionTarget>()
|
||||||
val calleeNamesForLambda = mutableListOf<Name>()
|
val calleeNamesForLambda = mutableListOf<Name>()
|
||||||
val firLabels = mutableListOf<FirLabel>()
|
|
||||||
|
@PrivateForInline
|
||||||
|
val _firLabels = mutableListOf<FirLabel>()
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
val firLabels: List<FirLabel>
|
||||||
|
get() = _firLabels
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A designated `KtElement` or `LighterASTNode` object that is allowed to claim the last label in [firLabels].
|
||||||
|
*/
|
||||||
|
@PrivateForInline
|
||||||
|
var firLabelUserNode: Any? = null
|
||||||
val firLoopTargets = mutableListOf<FirLoopTarget>()
|
val firLoopTargets = mutableListOf<FirLoopTarget>()
|
||||||
val capturedTypeParameters = mutableListOf<StatusFirTypeParameterSymbolList>()
|
val capturedTypeParameters = mutableListOf<StatusFirTypeParameterSymbolList>()
|
||||||
val arraySetArgument = mutableMapOf<T, FirExpression>()
|
val arraySetArgument = mutableMapOf<T, FirExpression>()
|
||||||
@@ -60,5 +69,50 @@ class Context<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last label that was added or null if the current node does not have permission to use the label.
|
||||||
|
*/
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
fun getLastLabel(currentNode: Any): FirLabel? {
|
||||||
|
if (this.firLabelUserNode == currentNode) return firLabels.last()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
fun addNewLabel(label: FirLabel) {
|
||||||
|
_firLabels += label
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
fun setNewLabelUserNode(useNode: Any?) {
|
||||||
|
this.firLabelUserNode = useNode
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
fun dropLastLabel() {
|
||||||
|
_firLabels.removeLast()
|
||||||
|
firLabelUserNode = null
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> withNewLabel(label: FirLabel, userNode: Any?, block: () -> T): T {
|
||||||
|
addNewLabel(label)
|
||||||
|
setNewLabelUserNode(userNode)
|
||||||
|
try {
|
||||||
|
return block()
|
||||||
|
} finally {
|
||||||
|
dropLastLabel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards the permission to use the last label to a different node if the current user node has the permission.
|
||||||
|
*/
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
fun forwardLabelUsagePermission(currentUserNode: Any, newUserNode: Any?) {
|
||||||
|
if (currentUserNode == firLabelUserNode) {
|
||||||
|
firLabelUserNode = newUserNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class StatusFirTypeParameterSymbolList(val notNested: Boolean, val list: List<FirTypeParameterSymbol> = listOf())
|
data class StatusFirTypeParameterSymbolList(val notNested: Boolean, val list: List<FirTypeParameterSymbol> = listOf())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// NO_CHECK_LAMBDA_INLINING
|
// NO_CHECK_LAMBDA_INLINING
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
|
|
||||||
inline fun foo(f: () -> Unit) {
|
inline fun foo(f: () -> Unit) {
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
fun test() {
|
||||||
|
a@ b@ while(true) {
|
||||||
|
val f = {
|
||||||
|
<!NOT_A_FUNCTION_LABEL!>return@a<!>
|
||||||
|
}
|
||||||
|
break@b
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test(): kotlin.Unit
|
||||||
+11
-2
@@ -51,12 +51,21 @@ fun testLoopLabelInReturn(xs: List<Int>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testValLabelInReturn() {
|
fun testValLabelInReturn() {
|
||||||
L@ val fn = { return@L }
|
L@ val fn = { <!NOT_A_FUNCTION_LABEL!>return@L<!> }
|
||||||
fn()
|
fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testHighOrderFunctionCallLabelInReturn() {
|
fun testHighOrderFunctionCallLabelInReturn() {
|
||||||
L@ run {
|
L@ run {
|
||||||
return@L
|
<!NOT_A_FUNCTION_LABEL!>return@L<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMultipleLabelsWithNestedLambda() {
|
||||||
|
l1@ l2@{
|
||||||
|
{
|
||||||
|
<!NOT_A_FUNCTION_LABEL!>return@l1<!>
|
||||||
|
}
|
||||||
|
return@l2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-1
@@ -59,4 +59,13 @@ fun testHighOrderFunctionCallLabelInReturn() {
|
|||||||
<!REDUNDANT_LABEL_WARNING!>L@<!> run {
|
<!REDUNDANT_LABEL_WARNING!>L@<!> run {
|
||||||
<!NOT_A_FUNCTION_LABEL!>return@L<!>
|
<!NOT_A_FUNCTION_LABEL!>return@L<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testMultipleLabelsWithNestedLambda() {
|
||||||
|
l1@ l2@{
|
||||||
|
{
|
||||||
|
<!RETURN_NOT_ALLOWED!>return@l1<!>
|
||||||
|
}
|
||||||
|
return@l2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
@@ -9,6 +9,7 @@ public fun testLambdaLabel(): () -> kotlin.Unit
|
|||||||
public fun testLambdaMultipleLabels1(): () -> kotlin.Unit
|
public fun testLambdaMultipleLabels1(): () -> kotlin.Unit
|
||||||
public fun testLambdaMultipleLabels2(): () -> kotlin.Unit
|
public fun testLambdaMultipleLabels2(): () -> kotlin.Unit
|
||||||
public fun testLoopLabelInReturn(/*0*/ xs: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
public fun testLoopLabelInReturn(/*0*/ xs: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
||||||
|
public fun testMultipleLabelsWithNestedLambda(): kotlin.Unit
|
||||||
public fun testParenthesizedLambdaLabel(): () -> kotlin.Unit
|
public fun testParenthesizedLambdaLabel(): () -> kotlin.Unit
|
||||||
public fun testValLabelInReturn(): kotlin.Unit
|
public fun testValLabelInReturn(): kotlin.Unit
|
||||||
|
|
||||||
@@ -18,3 +19,4 @@ public fun testValLabelInReturn(): kotlin.Unit
|
|||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-2
@@ -51,12 +51,21 @@ fun testLoopLabelInReturn(xs: List<Int>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testValLabelInReturn() {
|
fun testValLabelInReturn() {
|
||||||
L@ val fn = { return@L }
|
L@ val fn = { <!NOT_A_FUNCTION_LABEL!>return@L<!> }
|
||||||
fn()
|
fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testHighOrderFunctionCallLabelInReturn() {
|
fun testHighOrderFunctionCallLabelInReturn() {
|
||||||
L@ run {
|
L@ run {
|
||||||
return@L
|
<!NOT_A_FUNCTION_LABEL!>return@L<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMultipleLabelsWithNestedLambda() {
|
||||||
|
l1@ l2@{
|
||||||
|
{
|
||||||
|
<!NOT_A_FUNCTION_LABEL!>return@l1<!>
|
||||||
|
}
|
||||||
|
return@l2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-1
@@ -59,4 +59,13 @@ fun testHighOrderFunctionCallLabelInReturn() {
|
|||||||
<!REDUNDANT_LABEL_WARNING!>L@<!> run {
|
<!REDUNDANT_LABEL_WARNING!>L@<!> run {
|
||||||
<!NOT_A_FUNCTION_LABEL_WARNING!>return@L<!>
|
<!NOT_A_FUNCTION_LABEL_WARNING!>return@L<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testMultipleLabelsWithNestedLambda() {
|
||||||
|
l1@ l2@{
|
||||||
|
{
|
||||||
|
<!RETURN_NOT_ALLOWED!>return@l1<!>
|
||||||
|
}
|
||||||
|
return@l2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
@@ -9,6 +9,7 @@ public fun testLambdaLabel(): () -> kotlin.Unit
|
|||||||
public fun testLambdaMultipleLabels1(): () -> kotlin.Unit
|
public fun testLambdaMultipleLabels1(): () -> kotlin.Unit
|
||||||
public fun testLambdaMultipleLabels2(): () -> kotlin.Unit
|
public fun testLambdaMultipleLabels2(): () -> kotlin.Unit
|
||||||
public fun testLoopLabelInReturn(/*0*/ xs: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
public fun testLoopLabelInReturn(/*0*/ xs: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
||||||
|
public fun testMultipleLabelsWithNestedLambda(): kotlin.Unit
|
||||||
public fun testParenthesizedLambdaLabel(): () -> kotlin.Unit
|
public fun testParenthesizedLambdaLabel(): () -> kotlin.Unit
|
||||||
public fun testValLabelInReturn(): kotlin.Unit
|
public fun testValLabelInReturn(): kotlin.Unit
|
||||||
|
|
||||||
@@ -18,3 +19,4 @@ public fun testValLabelInReturn(): kotlin.Unit
|
|||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+6
@@ -6277,6 +6277,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
runTest("compiler/testData/diagnostics/tests/controlStructures/localReturnInsidePropertyAccessor.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nestedLoopsWithMultipleLabels.kt")
|
||||||
|
public void testNestedLoopsWithMultipleLabels() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/controlStructures/nestedLoopsWithMultipleLabels.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("notAFunctionLabel_after.kt")
|
@TestMetadata("notAFunctionLabel_after.kt")
|
||||||
public void testNotAFunctionLabel_after() throws Exception {
|
public void testNotAFunctionLabel_after() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user