Disallow non-local break/continue in crossinline lambdas
This commit is contained in:
committed by
Space
parent
79885c770d
commit
fe5a6fd511
+12
@@ -6290,6 +6290,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/controlStructures"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInCrossInlineLambda.kt")
|
||||
public void testBreakContinueInCrossInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/breakContinueInCrossInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInNoInlineLambda.kt")
|
||||
public void testBreakContinueInNoInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/breakContinueInNoInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakToLabel.kt")
|
||||
public void testBreakToLabel() throws Exception {
|
||||
|
||||
+12
@@ -6290,6 +6290,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/controlStructures"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInCrossInlineLambda.kt")
|
||||
public void testBreakContinueInCrossInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/breakContinueInCrossInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInNoInlineLambda.kt")
|
||||
public void testBreakContinueInNoInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/breakContinueInNoInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakToLabel.kt")
|
||||
public void testBreakToLabel() throws Exception {
|
||||
|
||||
+12
@@ -6290,6 +6290,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/controlStructures"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInCrossInlineLambda.kt")
|
||||
public void testBreakContinueInCrossInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/breakContinueInCrossInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInNoInlineLambda.kt")
|
||||
public void testBreakContinueInNoInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/breakContinueInNoInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakToLabel.kt")
|
||||
public void testBreakToLabel() throws Exception {
|
||||
|
||||
+3
-1
@@ -67,7 +67,9 @@ object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker
|
||||
val symbol = if (allowInlined) element.calleeReference.resolvedSymbol as? FirFunctionSymbol else null
|
||||
element.arguments.forEachIndexed { i, argument ->
|
||||
val expressionToCheck =
|
||||
if (symbol?.resolvedStatus?.isInline == true && !symbol.valueParameterSymbols[i].isNoinline) argument.tryInline() else argument
|
||||
if (symbol?.resolvedStatus?.isInline == true
|
||||
&& !symbol.valueParameterSymbols[i].run { isNoinline || isCrossinline }
|
||||
) argument.tryInline() else argument
|
||||
if (findPathAndCheck(expressionToCheck)) {
|
||||
return true
|
||||
}
|
||||
|
||||
Generated
-6
@@ -1250,12 +1250,6 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest {
|
||||
runTest("compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInNoInlineLambda.kt")
|
||||
public void testBreakContinueInNoInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInWhen.kt")
|
||||
public void testBreakContinueInWhen() throws Exception {
|
||||
|
||||
-6
@@ -1250,12 +1250,6 @@ public class LightTreeFir2IrTextTestGenerated extends AbstractLightTreeFir2IrTex
|
||||
runTest("compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInNoInlineLambda.kt")
|
||||
public void testBreakContinueInNoInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInWhen.kt")
|
||||
public void testBreakContinueInWhen() throws Exception {
|
||||
|
||||
+1
-1
@@ -197,7 +197,7 @@ fun isInlineableFunctionLiteral(expression: KtExpression, context: BindingContex
|
||||
val parameter = resolvedCall.valueArguments.entries.find { (_, valueArgument) ->
|
||||
valueArgument.arguments.any { it.asElement() == argument }
|
||||
}?.key ?: return false
|
||||
return !parameter.isNoinline
|
||||
return !parameter.isNoinline && !parameter.isCrossinline
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// FIR_IDENTICAL
|
||||
// LANGUAGE: +BreakContinueInInlineLambdas
|
||||
|
||||
|
||||
inline fun foo(crossinline block: () -> Unit) = block()
|
||||
|
||||
|
||||
fun test1() {
|
||||
L1@ while (true) {
|
||||
foo { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!> }
|
||||
|
||||
foo { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break@L1<!> }
|
||||
|
||||
foo { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue<!> }
|
||||
|
||||
foo { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue@L1<!> }
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public inline fun foo(/*0*/ crossinline block: () -> kotlin.Unit): kotlin.Unit
|
||||
public fun test1(): kotlin.Unit
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// FIR_IDENTICAL
|
||||
// LANGUAGE: +BreakContinueInInlineLambdas
|
||||
|
||||
|
||||
<!NOTHING_TO_INLINE!>inline<!> fun foo(noinline block: () -> Unit) = block()
|
||||
|
||||
|
||||
fun test1() {
|
||||
L1@ while (true) {
|
||||
foo { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!> }
|
||||
|
||||
foo { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break@L1<!> }
|
||||
|
||||
foo { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue<!> }
|
||||
|
||||
foo { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue@L1<!> }
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public inline fun foo(/*0*/ noinline block: () -> kotlin.Unit): kotlin.Unit
|
||||
public fun test1(): kotlin.Unit
|
||||
@@ -12,6 +12,11 @@ FILE fqName:<root> fileName:/badInlinedBreakContinue.kt
|
||||
$this: GET_VAR 'block1: kotlin.Function0<kotlin.Unit> declared in <root>.bar' type=kotlin.Function0<kotlin.Unit> origin=VARIABLE_AS_FUNCTION
|
||||
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE
|
||||
$this: GET_VAR 'block2: kotlin.Function0<kotlin.Unit> [noinline] declared in <root>.bar' type=kotlin.Function0<kotlin.Unit> origin=VARIABLE_AS_FUNCTION
|
||||
FUN name:baz visibility:public modality:FINAL <> (block:kotlin.Function0<kotlin.Unit>) returnType:kotlin.Unit [inline]
|
||||
VALUE_PARAMETER name:block index:0 type:kotlin.Function0<kotlin.Unit> [crossinline]
|
||||
BLOCK_BODY
|
||||
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE
|
||||
$this: GET_VAR 'block: kotlin.Function0<kotlin.Unit> [crossinline] declared in <root>.baz' type=kotlin.Function0<kotlin.Unit> origin=VARIABLE_AS_FUNCTION
|
||||
FUN name:myForEach visibility:public modality:FINAL <T> ($receiver:kotlin.collections.Iterable<T of <root>.myForEach>, action:kotlin.Function1<T of <root>.myForEach, kotlin.Unit>) returnType:kotlin.Unit [inline]
|
||||
TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?] reified:false
|
||||
$receiver: VALUE_PARAMETER name:<this> type:kotlin.collections.Iterable<T of <root>.myForEach>
|
||||
@@ -342,3 +347,13 @@ FILE fqName:<root> fileName:/badInlinedBreakContinue.kt
|
||||
arg1: CONST Int type=kotlin.Int value=2
|
||||
then: BLOCK type=kotlin.Unit origin=null
|
||||
ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing
|
||||
FUN name:test8 visibility:public modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
WHILE label=null origin=WHILE_LOOP
|
||||
condition: CONST Boolean type=kotlin.Boolean value=true
|
||||
body: BLOCK type=kotlin.Unit origin=null
|
||||
CALL 'public final fun baz (block: kotlin.Function0<kotlin.Unit>): kotlin.Unit [inline] declared in <root>' type=kotlin.Unit origin=null
|
||||
block: FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// LANGUAGE: +BreakContinueInInlineLambdas
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// !IGNORE_ERRORS
|
||||
// WITH_STDLIB
|
||||
@@ -11,6 +10,8 @@ inline fun bar(block1: () -> Unit, noinline block2: () -> Unit) {
|
||||
block2()
|
||||
}
|
||||
|
||||
inline fun baz(crossinline block: () -> Unit) { block() }
|
||||
|
||||
inline fun <T> Iterable<T>.myForEach(action: (T) -> Unit): Unit {
|
||||
for (element in this) action(element)
|
||||
}
|
||||
@@ -108,4 +109,10 @@ fun test7() {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun test8() {
|
||||
while (true) {
|
||||
baz({break})
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,10 @@ inline fun bar(block1: Function0<Unit>, noinline block2: Function0<Unit>) {
|
||||
block2.invoke()
|
||||
}
|
||||
|
||||
inline fun baz(crossinline block: Function0<Unit>) {
|
||||
block.invoke()
|
||||
}
|
||||
|
||||
inline fun <T : Any?> Iterable<T>.myForEach(action: Function1<T, Unit>) {
|
||||
{ // BLOCK
|
||||
val tmp0_iterator: Iterator<T> = <this>.iterator()
|
||||
@@ -220,3 +224,12 @@ fun test7() {
|
||||
)
|
||||
}
|
||||
|
||||
fun test8() {
|
||||
while (true) { // BLOCK
|
||||
baz(block = local fun <anonymous>() {
|
||||
error("") /* ErrorExpression */
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
FILE fqName:<root> fileName:/breakContinueInNoInlineLambda.kt
|
||||
FUN name:foo visibility:public modality:FINAL <> (block:kotlin.Function0<kotlin.Unit>) returnType:kotlin.Unit [inline]
|
||||
VALUE_PARAMETER name:block index:0 type:kotlin.Function0<kotlin.Unit> [noinline]
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='public final fun foo (block: kotlin.Function0<kotlin.Unit>): kotlin.Unit [inline] declared in <root>'
|
||||
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE
|
||||
$this: GET_VAR 'block: kotlin.Function0<kotlin.Unit> [noinline] declared in <root>.foo' type=kotlin.Function0<kotlin.Unit> origin=VARIABLE_AS_FUNCTION
|
||||
FUN name:test1 visibility:public modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
WHILE label=L1 origin=WHILE_LOOP
|
||||
condition: CONST Boolean type=kotlin.Boolean value=true
|
||||
body: BLOCK type=kotlin.Unit origin=null
|
||||
CALL 'public final fun foo (block: kotlin.Function0<kotlin.Unit>): kotlin.Unit [inline] declared in <root>' type=kotlin.Unit origin=null
|
||||
block: FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing
|
||||
CALL 'public final fun foo (block: kotlin.Function0<kotlin.Unit>): kotlin.Unit [inline] declared in <root>' type=kotlin.Unit origin=null
|
||||
block: FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
ERROR_EXPR 'Loop not found for break expression: break@L1' type=kotlin.Nothing
|
||||
CALL 'public final fun foo (block: kotlin.Function0<kotlin.Unit>): kotlin.Unit [inline] declared in <root>' type=kotlin.Unit origin=null
|
||||
block: FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing
|
||||
CALL 'public final fun foo (block: kotlin.Function0<kotlin.Unit>): kotlin.Unit [inline] declared in <root>' type=kotlin.Unit origin=null
|
||||
block: FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
ERROR_EXPR 'Loop not found for continue expression: continue@L1' type=kotlin.Nothing
|
||||
@@ -1,20 +0,0 @@
|
||||
// LANGUAGE: +BreakContinueInInlineLambdas
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// !IGNORE_ERRORS
|
||||
|
||||
|
||||
inline fun foo(noinline block: () -> Unit) = block()
|
||||
|
||||
|
||||
fun test1() {
|
||||
L1@ while (true) {
|
||||
foo { break }
|
||||
|
||||
foo { break@L1 }
|
||||
|
||||
foo { continue }
|
||||
|
||||
foo { continue@L1 }
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
inline fun foo(noinline block: Function0<Unit>) {
|
||||
return block.invoke()
|
||||
}
|
||||
|
||||
fun test1() {
|
||||
L1@ while (true) { // BLOCK
|
||||
foo(block = local fun <anonymous>() {
|
||||
error("") /* ErrorExpression */
|
||||
}
|
||||
)
|
||||
foo(block = local fun <anonymous>() {
|
||||
error("") /* ErrorExpression */
|
||||
}
|
||||
)
|
||||
foo(block = local fun <anonymous>() {
|
||||
error("") /* ErrorExpression */
|
||||
}
|
||||
)
|
||||
foo(block = local fun <anonymous>() {
|
||||
error("") /* ErrorExpression */
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Generated
+12
@@ -6296,6 +6296,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/controlStructures"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInCrossInlineLambda.kt")
|
||||
public void testBreakContinueInCrossInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/breakContinueInCrossInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInNoInlineLambda.kt")
|
||||
public void testBreakContinueInNoInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/breakContinueInNoInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakToLabel.kt")
|
||||
public void testBreakToLabel() throws Exception {
|
||||
|
||||
Generated
-6
@@ -1250,12 +1250,6 @@ public class IrTextTestGenerated extends AbstractIrTextTest {
|
||||
runTest("compiler/testData/ir/irText/expressions/breakContinueInLoopHeader.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInNoInlineLambda.kt")
|
||||
public void testBreakContinueInNoInlineLambda() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("breakContinueInWhen.kt")
|
||||
public void testBreakContinueInWhen() throws Exception {
|
||||
|
||||
@@ -922,6 +922,11 @@ public class KlibTextTestCaseGenerated extends AbstractKlibTextTestCase {
|
||||
runTest("compiler/testData/ir/irText/expressions/badBreakContinue.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("badInlinedBreakContinue.kt")
|
||||
public void testBadInlinedBreakContinue() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("bangbang.kt")
|
||||
public void testBangbang() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/expressions/bangbang.kt");
|
||||
|
||||
Reference in New Issue
Block a user