Disallow non-local break/continue in crossinline lambdas

This commit is contained in:
Pavel Mikhailovskii
2022-09-09 19:24:02 +02:00
committed by Space
parent 79885c770d
commit fe5a6fd511
20 changed files with 137 additions and 97 deletions
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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
}
@@ -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 {
@@ -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 {
@@ -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
@@ -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<!> }
}
}
@@ -0,0 +1,4 @@
package
public inline fun foo(/*0*/ crossinline block: () -> kotlin.Unit): kotlin.Unit
public fun test1(): kotlin.Unit
@@ -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<!> }
}
}
@@ -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 */
}
)
}
}
@@ -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 {
@@ -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");