From 8ba80b4b7bd88424066c1f85e4ba787a0c2fefcd Mon Sep 17 00:00:00 2001 From: Pavel Mikhailovskii Date: Thu, 11 Aug 2022 00:12:43 +0200 Subject: [PATCH] KT-1436 Allow break/continue in inlined lambdas --- .idea/kotlinc.xml | 5 +- ...CompilerTestFE10TestdataTestGenerated.java | 12 + ...irOldFrontendDiagnosticsTestGenerated.java | 12 + ...DiagnosticsWithLightTreeTestGenerated.java | 12 + ...tinueJumpsAcrossFunctionBoundaryChecker.kt | 30 +- .../FirBlackBoxCodegenTestGenerated.java | 46 +++ .../runners/ir/Fir2IrTextTestGenerated.java | 18 + .../ir/LightTreeFir2IrTextTestGenerated.java | 18 + .../kotlin/cfg/ControlFlowProcessor.kt | 7 +- .../bindingContextUtil/BindingContextUtils.kt | 53 ++- .../inline/DeepCopyIrTreeWithDescriptors.kt | 1 + .../backend/jvm/lower/DirectInvokeLowering.kt | 2 +- .../kotlin/psi2ir/generators/BodyGenerator.kt | 14 +- .../psi2ir/generators/DeclarationGenerator.kt | 6 +- .../psi2ir/generators/FunctionGenerator.kt | 12 +- .../generators/LocalFunctionGenerator.kt | 18 +- .../psi2ir/generators/ScriptGenerator.kt | 5 +- .../ir/DeepCopyIrTreeWithDeclarations.kt | 10 +- .../ir/util/DeepCopyIrTreeWithSymbols.kt | 5 +- .../inlineFunctionWithMultipleParameters.kt | 35 ++ .../lambdaPassedToInlineFunction.kt | 65 ++++ .../loopWithinInlineFunction.kt | 30 ++ .../inlinedBreakContinue/simple.kt | 17 + .../inlinedBreakContinue/stdlibFunctions.kt | 22 ++ .../inlinedBreakContinue/withReturnValue.kt | 18 + .../breakContinueNoinline.kt | 21 ++ .../breakContinueNoinline.txt | 5 + .../breakInLambdaPassedToDirectInvoke.kt | 10 + .../breakInLambdaPassedToDirectInvoke.txt | 3 + .../breakInsideLocal.fir.kt | 123 +++++-- .../controlFlowAnalysis/breakInsideLocal.kt | 121 ++++-- .../controlFlowAnalysis/breakInsideLocal.txt | 11 +- .../badInlinedBreakContinue.ir.txt | 344 ++++++++++++++++++ .../expressions/badInlinedBreakContinue.kt | 111 ++++++ .../badInlinedBreakContinue.kt.txt | 222 +++++++++++ .../breakContinueInNoInlineLambda.ir.txt | 32 ++ .../breakContinueInNoInlineLambda.kt | 20 + .../breakContinueInNoInlineLambda.kt.txt | 24 ++ .../expressions/inlinedBreakContinue.ir.txt | 80 ++++ .../expressions/inlinedBreakContinue.kt | 31 ++ .../expressions/inlinedBreakContinue.kt.txt | 60 +++ .../test/runners/DiagnosticTestGenerated.java | 12 + .../codegen/BlackBoxCodegenTestGenerated.java | 10 + .../IrBlackBoxCodegenTestGenerated.java | 46 +++ .../test/runners/ir/IrTextTestGenerated.java | 18 + .../LightAnalysisModeTestGenerated.java | 13 + .../controlFlow/unreachableCode/neg/1.fir.kt | 1 + .../kotlin/config/LanguageVersionSettings.kt | 1 + .../js/test/JsCodegenBoxTestGenerated.java | 10 + .../test/ir/IrJsCodegenBoxTestGenerated.java | 46 +++ .../IrCodegenBoxWasmTestGenerated.java | 43 +++ .../NativeCodegenBoxTestGenerated.java | 48 +++ 52 files changed, 1851 insertions(+), 88 deletions(-) create mode 100644 compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt create mode 100644 compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt create mode 100644 compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt create mode 100644 compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt create mode 100644 compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt create mode 100644 compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.txt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.txt create mode 100644 compiler/testData/ir/irText/expressions/badInlinedBreakContinue.ir.txt create mode 100644 compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt create mode 100644 compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt.txt create mode 100644 compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.ir.txt create mode 100644 compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt create mode 100644 compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt.txt create mode 100644 compiler/testData/ir/irText/expressions/inlinedBreakContinue.ir.txt create mode 100644 compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt create mode 100644 compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt.txt diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index b7c117d82c0..4fadbe817a5 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -8,4 +8,7 @@ - + + + \ No newline at end of file diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 45b2a0c396d..89d95a614cc 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -5476,6 +5476,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.kt"); } + @Test + @TestMetadata("breakContinueNoinline.kt") + public void testBreakContinueNoinline() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.kt"); + } + + @Test + @TestMetadata("breakInLambdaPassedToDirectInvoke.kt") + public void testBreakInLambdaPassedToDirectInvoke() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.kt"); + } + @Test @TestMetadata("breakInsideLocal.kt") public void testBreakInsideLocal() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index b7f16f68cfb..f15c93bdf3c 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -5476,6 +5476,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.kt"); } + @Test + @TestMetadata("breakContinueNoinline.kt") + public void testBreakContinueNoinline() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.kt"); + } + + @Test + @TestMetadata("breakInLambdaPassedToDirectInvoke.kt") + public void testBreakInLambdaPassedToDirectInvoke() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.kt"); + } + @Test @TestMetadata("breakInsideLocal.kt") public void testBreakInsideLocal() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 6c409c6930d..b5df4f232bf 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -5476,6 +5476,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.kt"); } + @Test + @TestMetadata("breakContinueNoinline.kt") + public void testBreakContinueNoinline() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.kt"); + } + + @Test + @TestMetadata("breakInLambdaPassedToDirectInvoke.kt") + public void testBreakInLambdaPassedToDirectInvoke() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.kt"); + } + @Test @TestMetadata("breakInsideLocal.kt") public void testBreakInsideLocal() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker.kt index 574a1b46887..85259a56e9b 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker.kt @@ -5,16 +5,21 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors -import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression +import org.jetbrains.kotlin.fir.resolvedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker() { override fun check(expression: FirLoopJump, context: CheckerContext, reporter: DiagnosticReporter) { + val allowInlined = context.languageVersionSettings.supportsFeature(LanguageFeature.BreakContinueInInlineLambdas) val errorPathElements = ArrayDeque() fun findPathAndCheck(element: FirElement?): Boolean { @@ -43,6 +48,7 @@ object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker } } } + is FirLoop -> return findPathAndCheck(element.condition) || findPathAndCheck(element.block) is FirWhenExpression -> { for (branch in element.branches) { if (findPathAndCheck(branch.result)) { @@ -52,6 +58,21 @@ object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker } is FirVariable -> return findPathAndCheck(element.initializer) is FirWrappedExpression -> return findPathAndCheck(element.expression) + is FirNoReceiverExpression -> return false + is FirFunctionCall -> { + if (findPathAndCheck(element.extensionReceiver) || findPathAndCheck(element.dispatchReceiver)) { + return true + } + + 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 (findPathAndCheck(expressionToCheck)) { + return true + } + } + } is FirCall -> { for (argument in element.arguments) { if (findPathAndCheck(argument)) { @@ -98,4 +119,9 @@ object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker findPathAndCheck(expression.target.labeledElement.block) } + + private fun FirExpression.tryInline(): FirExpression { + val anonymousFunctionExpression = (((this as? FirLambdaArgumentExpression)?.expression) ?: this) as? FirAnonymousFunctionExpression + return anonymousFunctionExpression?.anonymousFunction?.body ?: this + } } \ No newline at end of file diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 11adbb23903..d357fe5d48f 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -9033,6 +9033,52 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT public void testWhileTrueBreak() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/whileTrueBreak.kt"); } + + @Nested + @TestMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue") + @TestDataPath("$PROJECT_ROOT") + public class InlinedBreakContinue { + @Test + public void testAllFilesPresentInInlinedBreakContinue() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("inlineFunctionWithMultipleParameters.kt") + public void testInlineFunctionWithMultipleParameters() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt"); + } + + @Test + @TestMetadata("lambdaPassedToInlineFunction.kt") + public void testLambdaPassedToInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt"); + } + + @Test + @TestMetadata("loopWithinInlineFunction.kt") + public void testLoopWithinInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt"); + } + + @Test + @TestMetadata("stdlibFunctions.kt") + public void testStdlibFunctions() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt"); + } + + @Test + @TestMetadata("withReturnValue.kt") + public void testWithReturnValue() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt"); + } + } } @Nested diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/Fir2IrTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/Fir2IrTextTestGenerated.java index 6d54b3b5fee..18a257fd94e 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/Fir2IrTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/Fir2IrTextTestGenerated.java @@ -1196,6 +1196,12 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest { runTest("compiler/testData/ir/irText/expressions/badBreakContinue.kt"); } + @Test + @TestMetadata("badInlinedBreakContinue.kt") + public void testBadInlinedBreakContinue() throws Exception { + runTest("compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt"); + } + @Test @TestMetadata("bangbang.kt") public void testBangbang() throws Exception { @@ -1238,6 +1244,12 @@ 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 { @@ -1502,6 +1514,12 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest { runTest("compiler/testData/ir/irText/expressions/incrementDecrement.kt"); } + @Test + @TestMetadata("inlinedBreakContinue.kt") + public void testInlinedBreakContinue() throws Exception { + runTest("compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt"); + } + @Test @TestMetadata("interfaceThisRef.kt") public void testInterfaceThisRef() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/LightTreeFir2IrTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/LightTreeFir2IrTextTestGenerated.java index 9b7c5256d3d..7b4b096147a 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/LightTreeFir2IrTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/LightTreeFir2IrTextTestGenerated.java @@ -1196,6 +1196,12 @@ public class LightTreeFir2IrTextTestGenerated extends AbstractLightTreeFir2IrTex runTest("compiler/testData/ir/irText/expressions/badBreakContinue.kt"); } + @Test + @TestMetadata("badInlinedBreakContinue.kt") + public void testBadInlinedBreakContinue() throws Exception { + runTest("compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt"); + } + @Test @TestMetadata("bangbang.kt") public void testBangbang() throws Exception { @@ -1238,6 +1244,12 @@ 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 { @@ -1502,6 +1514,12 @@ public class LightTreeFir2IrTextTestGenerated extends AbstractLightTreeFir2IrTex runTest("compiler/testData/ir/irText/expressions/incrementDecrement.kt"); } + @Test + @TestMetadata("inlinedBreakContinue.kt") + public void testInlinedBreakContinue() throws Exception { + runTest("compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt"); + } + @Test @TestMetadata("interfaceThisRef.kt") public void testInterfaceThisRef() throws Exception { diff --git a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index 95b29fc9de2..ed015f875b8 100644 --- a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageFeature.BreakContinueInInlineLambdas import org.jetbrains.kotlin.config.LanguageFeature.ProhibitQualifiedAccessToUninitializedEnumEntry import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange @@ -926,9 +927,9 @@ class ControlFlowProcessor( private fun jumpDoesNotCrossFunctionBoundary(jumpExpression: KtExpressionWithLabel, jumpTarget: KtLoopExpression): Boolean { val bindingContext = trace.bindingContext - - val labelExprEnclosingFunc = getEnclosingFunctionDescriptor(bindingContext, jumpExpression) - val labelTargetEnclosingFunc = getEnclosingFunctionDescriptor(bindingContext, jumpTarget) + val skipInlineFunctions = languageVersionSettings.supportsFeature(BreakContinueInInlineLambdas) + val labelExprEnclosingFunc = getEnclosingFunctionDescriptor(bindingContext, jumpExpression, skipInlineFunctions) + val labelTargetEnclosingFunc = getEnclosingFunctionDescriptor(bindingContext, jumpTarget, skipInlineFunctions) return if (labelExprEnclosingFunc !== labelTargetEnclosingFunc) { // Check to report only once if (builder.getLoopExitPoint(jumpTarget) != null || diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/bindingContextUtil/BindingContextUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/bindingContextUtil/BindingContextUtils.kt index d5c3404e009..030af2c0234 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/bindingContextUtil/BindingContextUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/bindingContextUtil/BindingContextUtils.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtPsiUtil.deparenthesizeOnce import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.BindingContext @@ -32,6 +33,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.utils.takeSnapshot import org.jetbrains.kotlin.types.KotlinType @@ -154,12 +156,49 @@ fun getEnclosingDescriptor(context: BindingContext, element: KtElement): Declara } } -fun getEnclosingFunctionDescriptor(context: BindingContext, element: KtElement): FunctionDescriptor? { - val functionOrClass = element.getParentOfTypeCodeFragmentAware(KtFunction::class.java, KtClassOrObject::class.java) - val descriptor = context.get(DECLARATION_TO_DESCRIPTOR, functionOrClass) - return if (functionOrClass is KtFunction) { - if (descriptor is FunctionDescriptor) descriptor else null - } else { - if (descriptor is ClassDescriptor) descriptor.unsubstitutedPrimaryConstructor else null +fun getEnclosingFunctionDescriptor(context: BindingContext, element: KtElement, skipInlineFunctionLiterals: Boolean): FunctionDescriptor? { + var current = element + while (true) { + val functionOrClass = current.getParentOfTypeCodeFragmentAware(KtFunction::class.java, KtClassOrObject::class.java) + val descriptor = context.get(DECLARATION_TO_DESCRIPTOR, functionOrClass) + if (functionOrClass is KtFunction) { + if (descriptor is FunctionDescriptor) { + if (skipInlineFunctionLiterals && isInlineableFunctionLiteral( + ((functionOrClass as? KtFunctionLiteral)?.parent as? KtExpression) ?: functionOrClass, + context + )) { + current = functionOrClass + } else { + return descriptor + } + } else { + return null + } + } else { + return if (descriptor is ClassDescriptor) descriptor.unsubstitutedPrimaryConstructor else null + } } } + +fun isInlineableFunctionLiteral(expression: KtExpression, context: BindingContext): Boolean { + if (expression !is KtLambdaExpression && !(expression is KtNamedFunction && expression.name == null)) { + return false + } + var wrapper: PsiElement = expression + while (deparenthesizeOnce(wrapper.parent as? KtExpression) == wrapper) { + wrapper = wrapper.parent + } + + val argument = (wrapper.parent as? KtValueArgument) ?: return false + val call = (((argument.parent as? KtValueArgumentList) ?: argument).parent as? KtCallExpression) ?: return false + val resolvedCall = call.getResolvedCall(context) ?: return false + val descriptor = (resolvedCall.resultingDescriptor as? FunctionDescriptor) ?: return false + if (descriptor.isInline) { + val parameter = resolvedCall.valueArguments.entries.find { (_, valueArgument) -> + valueArgument.arguments.any { it.asElement() == argument } + }?.key ?: return false + return !parameter.isNoinline + } + + return false +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/DeepCopyIrTreeWithDescriptors.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/DeepCopyIrTreeWithDescriptors.kt index 7fc8a696ec4..b086990fa13 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/DeepCopyIrTreeWithDescriptors.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/DeepCopyIrTreeWithDescriptors.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer import org.jetbrains.kotlin.ir.declarations.copyAttributes import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/DirectInvokeLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/DirectInvokeLowering.kt index c292d586b83..8b2c68b3658 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/DirectInvokeLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/DirectInvokeLowering.kt @@ -73,7 +73,7 @@ private class DirectInvokeLowering(private val context: JvmBackendContext) : Fil return context.createIrBuilder(scope.scopeOwnerSymbol).run { at(expression) irBlock { - val arguments = function.explicitParameters.withIndex().map { (index, parameter) -> + val arguments = function.explicitParameters.mapIndexed { index, parameter -> val argument = expression.getValueArgument(index)!! IrVariableImpl( argument.startOffset, argument.endOffset, IrDeclarationOrigin.DEFINED, IrVariableSymbolImpl(), parameter.name, diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt index a85a5449f2e..d2f2298edb2 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt @@ -39,10 +39,15 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isUnit +interface LoopResolver { + fun getLoop(expression: KtExpression): IrLoop? +} + class BodyGenerator( val scopeOwnerSymbol: IrSymbol, - override val context: GeneratorContext -) : GeneratorWithScope { + override val context: GeneratorContext, + private val parentLoopResolver: LoopResolver? +) : GeneratorWithScope, LoopResolver { val scopeOwner: DeclarationDescriptor get() = scopeOwnerSymbol.descriptor @@ -196,8 +201,9 @@ class BodyGenerator( loopTable[expression] = irLoop } - fun getLoop(expression: KtExpression): IrLoop? = - loopTable[expression] + override fun getLoop(expression: KtExpression): IrLoop? { + return loopTable[expression] ?: parentLoopResolver?.getLoop(expression) + } fun generatePrimaryConstructorBody(ktClassOrObject: KtPureClassOrObject, irConstructor: IrConstructor): IrBody { val irBlockBody = context.irFactory.createBlockBody(ktClassOrObject.pureStartOffset, ktClassOrObject.pureEndOffset) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt index 22f464ec8a0..a04713a05f5 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt @@ -46,7 +46,7 @@ class DeclarationGenerator(override val context: GeneratorContext) : Generator { return try { when (ktDeclaration) { is KtNamedFunction -> - FunctionGenerator(this).generateFunctionDeclaration(ktDeclaration) + FunctionGenerator(this).generateFunctionDeclaration(ktDeclaration, null) is KtProperty -> PropertyGenerator(this).generatePropertyDeclaration(ktDeclaration) is KtClassOrObject -> @@ -207,5 +207,5 @@ abstract class DeclarationGeneratorExtension(val declarationGenerator: Declarati fun KotlinType.toIrType() = with(declarationGenerator) { toIrType() } } -fun Generator.createBodyGenerator(scopeOwnerSymbol: IrSymbol) = - BodyGenerator(scopeOwnerSymbol, context) +fun Generator.createBodyGenerator(scopeOwnerSymbol: IrSymbol, parentLoopResolver: LoopResolver? = null) = + BodyGenerator(scopeOwnerSymbol, context, parentLoopResolver) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/FunctionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/FunctionGenerator.kt index 50a73316703..b2fff9dff5e 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/FunctionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/FunctionGenerator.kt @@ -40,6 +40,7 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio @JvmOverloads fun generateFunctionDeclaration( ktFunction: KtNamedFunction, + parentLoopResolver: LoopResolver?, origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED ): IrSimpleFunction = declareSimpleFunction( @@ -47,19 +48,21 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio ktFunction.receiverTypeReference, ktFunction.contextReceivers.mapNotNull { it.typeReference() }, origin, - getOrFail(BindingContext.FUNCTION, ktFunction) + getOrFail(BindingContext.FUNCTION, ktFunction), + parentLoopResolver ) { ktFunction.bodyExpression?.let { generateFunctionBody(it) } } - fun generateLambdaFunctionDeclaration(ktFunction: KtFunctionLiteral): IrSimpleFunction { + fun generateLambdaFunctionDeclaration(ktFunction: KtFunctionLiteral, parentLoopResolver: LoopResolver?): IrSimpleFunction { val lambdaDescriptor = getOrFail(BindingContext.FUNCTION, ktFunction) return declareSimpleFunction( ktFunction, null, emptyList(), IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA, - lambdaDescriptor + lambdaDescriptor, + parentLoopResolver ) { generateLambdaBody(ktFunction, lambdaDescriptor) } @@ -80,11 +83,12 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio ktContextReceivers: List, origin: IrDeclarationOrigin, descriptor: FunctionDescriptor, + parentLoopResolver: LoopResolver?, generateBody: BodyGenerator.() -> IrBody? ): IrSimpleFunction = declareSimpleFunctionInner(descriptor, ktFunction, origin).buildWithScope { irFunction -> generateFunctionParameterDeclarationsAndReturnType(irFunction, ktFunction, ktReceiver, ktContextReceivers) - irFunction.body = createBodyGenerator(irFunction.symbol).generateBody() + irFunction.body = createBodyGenerator(irFunction.symbol, parentLoopResolver).generateBody() } private fun declareSimpleFunctionInner( diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LocalFunctionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LocalFunctionGenerator.kt index 566d8a3f7da..c1534e47e22 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LocalFunctionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LocalFunctionGenerator.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.psi2ir.generators +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin @@ -24,13 +25,19 @@ import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.bindingContextUtil.isInlineableFunctionLiteral class LocalFunctionGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) { fun generateLambda(ktLambda: KtLambdaExpression): IrStatement { val ktFun = ktLambda.functionLiteral val lambdaExpressionType = getTypeInferredByFrontendOrFail(ktLambda).toIrType() - val irLambdaFunction = FunctionGenerator(context).generateLambdaFunctionDeclaration(ktFun) + val loopResolver = if (context.languageVersionSettings.supportsFeature(LanguageFeature.BreakContinueInInlineLambdas) + && isInlineableFunctionLiteral(ktLambda, context.bindingContext) + ) + statementGenerator.bodyGenerator + else null + val irLambdaFunction = FunctionGenerator(context).generateLambdaFunctionDeclaration(ktFun, loopResolver) return IrFunctionExpressionImpl( ktLambda.startOffset, ktLambda.endOffset, @@ -54,5 +61,12 @@ class LocalFunctionGenerator(statementGenerator: StatementGenerator) : Statement } private fun generateFunctionDeclaration(ktFun: KtNamedFunction) = - FunctionGenerator(context).generateFunctionDeclaration(ktFun, IrDeclarationOrigin.LOCAL_FUNCTION) + FunctionGenerator(context).generateFunctionDeclaration( + ktFun, + if (context.languageVersionSettings.supportsFeature(LanguageFeature.BreakContinueInInlineLambdas) + && isInlineableFunctionLiteral(ktFun, context.bindingContext) + ) statementGenerator.bodyGenerator + else null, + IrDeclarationOrigin.LOCAL_FUNCTION + ) } \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt index fda4a0dcd90..3d8bfcecd2f 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt @@ -168,7 +168,8 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG is KtScriptInitializer -> { val irExpressionBody = BodyGenerator( irScript.symbol, - context + context, + null ).generateExpressionBody(d.body!!) if (d == ktScript.declarations.last() && descriptor.resultValue != null) { descriptor.resultValue!!.let { resultDescriptor -> @@ -194,7 +195,7 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG is KtDestructuringDeclaration -> { // copied with modifications from StatementGenerator.visitDestructuringDeclaration // TODO: consider code deduplication - val bodyGenerator = BodyGenerator(irScript.symbol, context) + val bodyGenerator = BodyGenerator(irScript.symbol, context, null) val statementGenerator = bodyGenerator.createStatementGenerator() val irBlock = IrCompositeImpl( d.startOffsetSkippingComments, d.endOffset, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/DeepCopyIrTreeWithDeclarations.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/DeepCopyIrTreeWithDeclarations.kt index b1708b47957..35387484050 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/DeepCopyIrTreeWithDeclarations.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/DeepCopyIrTreeWithDeclarations.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.ir -import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper @@ -19,12 +18,5 @@ fun T.deepCopyWithVariables(): T { val typesRemapper = DeepCopyTypeRemapper(symbolsRemapper) - return this.transform( - object : DeepCopyIrTreeWithSymbols(symbolsRemapper, typesRemapper) { - override fun getNonTransformedLoop(irLoop: IrLoop): IrLoop { - return irLoop - } - }, - null - ) as T + return this.transform(DeepCopyIrTreeWithSymbols(symbolsRemapper, typesRemapper), null) as T } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt index 17086b2dcdd..275e660bdaf 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt @@ -738,10 +738,7 @@ open class DeepCopyIrTreeWithSymbols( private val transformedLoops = HashMap() private fun getTransformedLoop(irLoop: IrLoop): IrLoop = - transformedLoops.getOrElse(irLoop) { getNonTransformedLoop(irLoop) } - - protected open fun getNonTransformedLoop(irLoop: IrLoop): IrLoop = - throw AssertionError("Outer loop was not transformed: ${irLoop.render()}") + transformedLoops.getOrDefault(irLoop, irLoop) override fun visitWhileLoop(loop: IrWhileLoop): IrWhileLoop = IrWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type.remapType(), mapStatementOrigin(loop.origin)).also { newLoop -> diff --git a/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt new file mode 100644 index 00000000000..1d14ef49c52 --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt @@ -0,0 +1,35 @@ +// LANGUAGE: +BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// TARGET_BACKEND: JS_IR +// TARGET_BACKEND: JS_IR_ES6 +// TARGET_BACKEND: NATIVE +// TARGET_BACKEND: WASM +// WITH_STDLIB + +import kotlin.test.assertEquals + +inline fun foo( + block1: () -> Unit, + noinline block2: () -> Unit, + block3: () -> Unit +) { + block1() + block2() + block3() +} + +fun box(): String { + val visited = mutableListOf() + + for (i in 1..3) { + foo( + { visited += 1; if (i == 1) continue }, + { visited += 2 }, + { visited += 3; if (i == 2) break }, + ) + } + + assertEquals(listOf(1, 1, 2, 3), visited) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt new file mode 100644 index 00000000000..8a13eff0b4d --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt @@ -0,0 +1,65 @@ +// LANGUAGE: +BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// TARGET_BACKEND: JS_IR +// TARGET_BACKEND: JS_IR_ES6 +// TARGET_BACKEND: NATIVE +// TARGET_BACKEND: WASM +// WITH_STDLIB + +import kotlin.test.assertEquals + +@Target(AnnotationTarget.EXPRESSION) +@Retention(AnnotationRetention.SOURCE) +public annotation class SomeAnnotation + +inline fun foo(block: () -> Unit) = block() + +fun box(): String { + val visited = mutableListOf>() + + var i = 0 + outer@ while (true) { + i += 1 + inner@ for (j in 1..10) { + foo( + (@SomeAnnotation + fun() { + foo(fun() { + foo(@SomeAnnotation { + foo { + if (i == 2) { + continue@outer + } + + if (i == 4) { + break@outer + } + + if (j == 2) { + continue + } + + if (j == 4) { + continue@inner + } + + if (j == 6 && i == 1) { + break@inner + } + + if (j == 6 && i == 3) { + break + } + } + }) + }) + }) + ) + visited += i to j + } + } + + assertEquals(listOf(1 to 1, 1 to 3, 1 to 5, 3 to 1, 3 to 3, 3 to 5), visited) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt new file mode 100644 index 00000000000..d588d1e98ee --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt @@ -0,0 +1,30 @@ +// LANGUAGE: +BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// TARGET_BACKEND: JS_IR +// TARGET_BACKEND: JS_IR_ES6 +// TARGET_BACKEND: NATIVE +// TARGET_BACKEND: WASM +// WITH_STDLIB + +import kotlin.test.assertEquals + +inline fun Iterable.myForEach(action: (T) -> Unit): Unit { + for (element in this) action(element) +} + + +fun box(): String { + val visited = mutableListOf>() + + for (i in 1..3) { + (1..3).myForEach { j -> + if (j == 3) { + break + } + visited += i to j + } + } + + assertEquals(listOf(1 to 1, 1 to 2), visited) + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt new file mode 100644 index 00000000000..0f545f12028 --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt @@ -0,0 +1,17 @@ +// LANGUAGE: +BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// TARGET_BACKEND: JS_IR +// TARGET_BACKEND: JS_IR_ES6 +// TARGET_BACKEND: NATIVE +// TARGET_BACKEND: WASM + +inline fun foo(block: () -> Unit) { block() } + +fun box(): String { + while (true) { + foo { break } + return "FAIL" + } + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt new file mode 100644 index 00000000000..4079cd6cc64 --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt @@ -0,0 +1,22 @@ +// LANGUAGE: +BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// TARGET_BACKEND: JS_IR +// TARGET_BACKEND: JS_IR_ES6 +// TARGET_BACKEND: NATIVE +// TARGET_BACKEND: WASM +// WITH_STDLIB + +import kotlin.test.assertEquals + +inline fun Iterable.myForEach(action: (T) -> Unit): Unit { + for (element in this) action(element) +} + + +fun box(): String { + while (true) { + "".let { it.run { break } } + } + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt new file mode 100644 index 00000000000..2770f282139 --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt @@ -0,0 +1,18 @@ +// LANGUAGE: +BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// TARGET_BACKEND: JS_IR +// TARGET_BACKEND: JS_IR_ES6 +// TARGET_BACKEND: NATIVE +// TARGET_BACKEND: WASM + +inline fun foo(block: () -> Int): Int = block() + +fun box(): String { + var sum = 0 + + for (i in 1..10) { + sum += foo { if (i == 3) break else i } + } + + return if (sum == 3) "OK" else "FAIL" +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.kt new file mode 100644 index 00000000000..b02fa55e63b --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.kt @@ -0,0 +1,21 @@ +// FIR_IDENTICAL +// LANGUAGE: +BreakContinueInInlineLambdas + + +inline fun notInlining(noinline block1: () -> Unit, block2: () -> Unit) { + block1() + block2() +} + +fun test() { + label@ while(true) { + notInlining({ break }) {} + notInlining({ continue }) {} + notInlining(fun () { break }) {} + notInlining(fun () { continue }) {} + notInlining({ break@label }) {} + notInlining({ continue@label }) {} + notInlining(fun () { break@label }) {} + notInlining(fun () { continue@label }) {} + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.txt new file mode 100644 index 00000000000..016e089b89b --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.txt @@ -0,0 +1,5 @@ +package + +public inline fun notInlining(/*0*/ noinline block1: () -> kotlin.Unit, /*1*/ block2: () -> kotlin.Unit): kotlin.Unit +public fun test(): kotlin.Unit + diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.kt new file mode 100644 index 00000000000..01707eea017 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL +// LANGUAGE: +BreakContinueInInlineLambdas + +fun test() { + while(true) { + {block: () -> Unit -> block()}( + {break} + ) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.txt new file mode 100644 index 00000000000..93e27f34c8c --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.txt @@ -0,0 +1,3 @@ +package + +public fun test(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.fir.kt index 3ca844f3c3e..5840d975314 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.fir.kt @@ -1,38 +1,77 @@ +// LANGUAGE: +BreakContinueInInlineLambdas + +inline fun foo(block: () -> T): T = block() + + +@Target(AnnotationTarget.EXPRESSION) +@Retention(AnnotationRetention.SOURCE) +public annotation class SomeAnnotation + fun test() { while (true) { - fun local1() { - break + fun local1(tag: Int) { + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } } } fun test2() { while (true) { - { - continue + {tag: Int -> + when(tag) { + 0 -> continue + 1 -> foo { continue } + 2 -> foo(@SomeAnnotation { continue }) + 3 -> foo(fun () { continue }) + 4 -> foo(@SomeAnnotation fun () { continue }) + } } } } fun test3() { while (true) { - class LocalClass { + class LocalClass(val tag: Int) { init { - continue + when(tag) { + 0 -> continue + 1 -> foo { continue } + 2 -> foo(@SomeAnnotation { continue }) + 3 -> foo(fun () { continue }) + 4 -> foo(@SomeAnnotation fun () { continue }) + } } fun foo() { - break + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } } } } -fun test4() { +fun test4(tag: Int) { while (true) { object: Any() { init { - break + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } } } @@ -40,12 +79,15 @@ fun test4() { fun test5() { while (true) { - class LocalClass(val x: Int) { - constructor() : this(42) { - break - } - constructor(y: Double) : this(y.toInt()) { - continue + class LocalClass(val s: String) { + constructor(tag: Int) : this("") { + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } } } @@ -53,12 +95,24 @@ fun test5() { fun test6() { while (true) { - class LocalClass(val x: Int) { + class LocalClass(val tag: Int) { init { - break + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } init { - continue + when(tag) { + 0 -> continue + 1 -> foo { continue } + 2 -> foo(@SomeAnnotation { continue }) + 3 -> foo(fun () { continue }) + 4 -> foo(@SomeAnnotation fun () { continue }) + } } } } @@ -66,12 +120,26 @@ fun test6() { fun test7() { while (true) { - class LocalClass { + class LocalClass(val tag: Int) { val x: Int = if (true) { - break + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun (): Int { break }) + 4 -> foo(@SomeAnnotation fun (): Int { break }) + else -> 1 + } } else { - continue + when(tag) { + 0 -> continue + 1 -> foo { continue } + 2 -> foo(@SomeAnnotation { continue }) + 3 -> foo(fun (): Int { continue }) + 4 -> foo(@SomeAnnotation fun (): Int { continue }) + else -> 2 + } } } } @@ -80,7 +148,16 @@ fun test7() { fun test8() { while (true) { class LocalClass(val x: Int) { - constructor() : this(if (true) { 42 } else { break }) + constructor(tag: Int, unused: Boolean) : this( + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun (): Int { break }) + 4 -> foo(@SomeAnnotation fun (): Int { break }) + else -> 1 + } + ) } } -} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.kt index 170a379cf7b..a04c76504e1 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.kt @@ -1,38 +1,77 @@ +// LANGUAGE: +BreakContinueInInlineLambdas + +inline fun foo(block: () -> T): T = block() + + +@Target(AnnotationTarget.EXPRESSION) +@Retention(AnnotationRetention.SOURCE) +public annotation class SomeAnnotation + fun test() { while (true) { - fun local1() { - break + fun local1(tag: Int) { + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } } } fun test2() { while (true) { - { - continue + {tag: Int -> + when(tag) { + 0 -> continue + 1 -> foo { continue } + 2 -> foo(@SomeAnnotation { continue }) + 3 -> foo(fun () { continue }) + 4 -> foo(@SomeAnnotation fun () { continue }) + } } } } fun test3() { while (true) { - class LocalClass { + class LocalClass(val tag: Int) { init { - continue + when(tag) { + 0 -> continue + 1 -> foo { continue } + 2 -> foo(@SomeAnnotation { continue }) + 3 -> foo(fun () { continue }) + 4 -> foo(@SomeAnnotation fun () { continue }) + } } fun foo() { - break + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } } } } -fun test4() { +fun test4(tag: Int) { while (true) { object: Any() { init { - break + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } } } @@ -40,12 +79,15 @@ fun test4() { fun test5() { while (true) { - class LocalClass(val x: Int) { - constructor() : this(42) { - break - } - constructor(y: Double) : this(y.toInt()) { - continue + class LocalClass(val s: String) { + constructor(tag: Int) : this("") { + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } } } @@ -53,12 +95,24 @@ fun test5() { fun test6() { while (true) { - class LocalClass(val x: Int) { + class LocalClass(val tag: Int) { init { - break + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun () { break }) + 4 -> foo(@SomeAnnotation fun () { break }) + } } init { - continue + when(tag) { + 0 -> continue + 1 -> foo { continue } + 2 -> foo(@SomeAnnotation { continue }) + 3 -> foo(fun () { continue }) + 4 -> foo(@SomeAnnotation fun () { continue }) + } } } } @@ -66,12 +120,26 @@ fun test6() { fun test7() { while (true) { - class LocalClass { + class LocalClass(val tag: Int) { val x: Int = if (true) { - break + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun (): Int { break }) + 4 -> foo(@SomeAnnotation fun (): Int { break }) + else -> 1 + } } else { - continue + when(tag) { + 0 -> continue + 1 -> foo { continue } + 2 -> foo(@SomeAnnotation { continue }) + 3 -> foo(fun (): Int { continue }) + 4 -> foo(@SomeAnnotation fun (): Int { continue }) + else -> 2 + } } } } @@ -80,7 +148,16 @@ fun test7() { fun test8() { while (true) { class LocalClass(val x: Int) { - constructor() : this(if (true) { 42 } else { break }) + constructor(tag: Int, unused: Boolean) : this( + when(tag) { + 0 -> break + 1 -> foo { break } + 2 -> foo(@SomeAnnotation { break }) + 3 -> foo(fun (): Int { break }) + 4 -> foo(@SomeAnnotation fun (): Int { break }) + else -> 1 + } + ) } } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.txt index 8eb87ce2285..d92785120aa 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.txt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.txt @@ -1,10 +1,19 @@ package +public inline fun foo(/*0*/ block: () -> T): T public fun test(): kotlin.Unit public fun test2(): kotlin.Unit public fun test3(): kotlin.Unit -public fun test4(): kotlin.Unit +public fun test4(/*0*/ tag: kotlin.Int): kotlin.Unit public fun test5(): kotlin.Unit public fun test6(): kotlin.Unit public fun test7(): kotlin.Unit public fun test8(): kotlin.Unit + +@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.EXPRESSION}) @kotlin.annotation.Retention(value = AnnotationRetention.SOURCE) public final annotation class SomeAnnotation : kotlin.Annotation { + public constructor SomeAnnotation() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + diff --git a/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.ir.txt b/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.ir.txt new file mode 100644 index 00000000000..3e4f5ee7550 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.ir.txt @@ -0,0 +1,344 @@ +FILE fqName: fileName:/badInlinedBreakContinue.kt + FUN name:foo visibility:public modality:FINAL <> (block:kotlin.Function0) returnType:kotlin.Unit [inline] + VALUE_PARAMETER name:block index:0 type:kotlin.Function0 + 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 declared in .foo' type=kotlin.Function0 origin=VARIABLE_AS_FUNCTION + FUN name:bar visibility:public modality:FINAL <> (block1:kotlin.Function0, block2:kotlin.Function0) returnType:kotlin.Unit [inline] + VALUE_PARAMETER name:block1 index:0 type:kotlin.Function0 + VALUE_PARAMETER name:block2 index:1 type:kotlin.Function0 [noinline] + BLOCK_BODY + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: GET_VAR 'block1: kotlin.Function0 declared in .bar' type=kotlin.Function0 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 [noinline] declared in .bar' type=kotlin.Function0 origin=VARIABLE_AS_FUNCTION + FUN name:myForEach visibility:public modality:FINAL ($receiver:kotlin.collections.Iterable.myForEach>, action:kotlin.Function1.myForEach, kotlin.Unit>) returnType:kotlin.Unit [inline] + TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?] reified:false + $receiver: VALUE_PARAMETER name: type:kotlin.collections.Iterable.myForEach> + VALUE_PARAMETER name:action index:0 type:kotlin.Function1.myForEach, kotlin.Unit> + BLOCK_BODY + BLOCK type=kotlin.Unit origin=FOR_LOOP + VAR FOR_LOOP_ITERATOR name:tmp_0 type:kotlin.collections.Iterator.myForEach> [val] + CALL 'public abstract fun iterator (): kotlin.collections.Iterator [operator] declared in kotlin.collections.Iterable' type=kotlin.collections.Iterator.myForEach> origin=FOR_LOOP_ITERATOR + $this: GET_VAR ': kotlin.collections.Iterable.myForEach> declared in .myForEach' type=kotlin.collections.Iterable.myForEach> origin=null + WHILE label=null origin=FOR_LOOP_INNER_WHILE + condition: CALL 'public abstract fun hasNext (): kotlin.Boolean [operator] declared in kotlin.collections.Iterator' type=kotlin.Boolean origin=FOR_LOOP_HAS_NEXT + $this: GET_VAR 'val tmp_0: kotlin.collections.Iterator.myForEach> [val] declared in .myForEach' type=kotlin.collections.Iterator.myForEach> origin=null + body: BLOCK type=kotlin.Unit origin=FOR_LOOP_INNER_WHILE + VAR FOR_LOOP_VARIABLE name:element type:T of .myForEach [val] + CALL 'public abstract fun next (): T of kotlin.collections.Iterator [operator] declared in kotlin.collections.Iterator' type=T of .myForEach origin=FOR_LOOP_NEXT + $this: GET_VAR 'val tmp_0: kotlin.collections.Iterator.myForEach> [val] declared in .myForEach' type=kotlin.collections.Iterator.myForEach> origin=null + CALL 'public abstract fun invoke (p1: P1 of kotlin.Function1): R of kotlin.Function1 [operator] declared in kotlin.Function1' type=kotlin.Unit origin=INVOKE + $this: GET_VAR 'action: kotlin.Function1.myForEach, kotlin.Unit> declared in .myForEach' type=kotlin.Function1.myForEach, kotlin.Unit> origin=VARIABLE_AS_FUNCTION + p1: GET_VAR 'val element: T of .myForEach [val] declared in .myForEach' type=T of .myForEach origin=null + FUN name:test1 visibility:public modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR '{break}() + {continue}()' type=IrErrorType([Error type: Error expression type]) + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + FUN name:test2 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 + ERROR_EXPR '{break@ERROR}() + {continue@ERROR}()' type=IrErrorType([Error type: Error expression type]) + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break@ERROR' type=kotlin.Nothing + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue@ERROR' type=kotlin.Nothing + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break@ERROR' type=kotlin.Nothing + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue@ERROR' type=kotlin.Nothing + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break@ERROR' type=kotlin.Nothing + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue@ERROR' type=kotlin.Nothing + FUN name:test3 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 + VAR name:lambda type:kotlin.Function0 [val] + FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR '{break@L1}() + {continue@L1}()' type=IrErrorType([Error type: Error expression type]) + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break@L1' type=kotlin.Nothing + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue@L1' type=kotlin.Nothing + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue@L1' type=kotlin.Nothing + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue@L1' type=kotlin.Nothing + FUN name:test4 visibility:public modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + WHILE label=null origin=WHILE_LOOP + condition: CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Nothing origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Nothing + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.Nothing declared in .test4' + ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit origin=null + WHILE label=null origin=WHILE_LOOP + condition: CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Nothing origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Nothing + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.Nothing declared in .test4' + ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit origin=null + WHILE label=null origin=WHILE_LOOP + condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit origin=null + WHILE label=null origin=WHILE_LOOP + condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit origin=null + WHILE label=null origin=WHILE_LOOP + condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit origin=null + WHILE label=null origin=WHILE_LOOP + condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit origin=null + WHILE label=null origin=WHILE_LOOP + condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit origin=null + WHILE label=null origin=WHILE_LOOP + condition: TYPE_OP type=kotlin.Boolean origin=IMPLICIT_CAST typeOperand=kotlin.Boolean + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + body: BLOCK type=kotlin.Unit origin=null + FUN name:test5 visibility:public modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + CALL 'public final fun forEach (action: kotlin.Function1): kotlin.Unit [inline] declared in kotlin.collections.CollectionsKt' type=kotlin.Unit origin=null + : kotlin.Int + $receiver: CALL 'public final fun listOf (vararg elements: T of kotlin.collections.CollectionsKt.listOf): kotlin.collections.List declared in kotlin.collections.CollectionsKt' type=kotlin.collections.List origin=null + : kotlin.Int + elements: VARARG type=kotlin.Array varargElementType=kotlin.Int + CONST Int type=kotlin.Int value=1 + CONST Int type=kotlin.Int value=2 + CONST Int type=kotlin.Int value=3 + action: FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (i:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:i index:0 type:kotlin.Int + BLOCK_BODY + WHEN type=kotlin.Unit origin=IF + BRANCH + if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'i: kotlin.Int declared in .test5.' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=2 + then: ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + CALL 'public final fun forEach (action: kotlin.Function1): kotlin.Unit [inline] declared in kotlin.collections.CollectionsKt' type=kotlin.Unit origin=null + : kotlin.Int + $receiver: CALL 'public final fun listOf (vararg elements: T of kotlin.collections.CollectionsKt.listOf): kotlin.collections.List declared in kotlin.collections.CollectionsKt' type=kotlin.collections.List origin=null + : kotlin.Int + elements: VARARG type=kotlin.Array varargElementType=kotlin.Int + CONST Int type=kotlin.Int value=1 + CONST Int type=kotlin.Int value=2 + CONST Int type=kotlin.Int value=3 + action: FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (i:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:i index:0 type:kotlin.Int + BLOCK_BODY + WHEN type=kotlin.Unit origin=IF + BRANCH + if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'i: kotlin.Int declared in .test5.' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=2 + then: ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + CALL 'public final fun forEach (action: kotlin.Function1): kotlin.Unit [inline] declared in kotlin.collections.CollectionsKt' type=kotlin.Unit origin=null + : kotlin.Int + $receiver: CALL 'public final fun listOf (vararg elements: T of kotlin.collections.CollectionsKt.listOf): kotlin.collections.List declared in kotlin.collections.CollectionsKt' type=kotlin.collections.List origin=null + : kotlin.Int + elements: VARARG type=kotlin.Array varargElementType=kotlin.Int + CONST Int type=kotlin.Int value=1 + CONST Int type=kotlin.Int value=2 + CONST Int type=kotlin.Int value=3 + action: FUN_EXPR type=kotlin.Function1 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> (i:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:i index:0 type:kotlin.Int + BLOCK_BODY + WHEN type=kotlin.Unit origin=IF + BRANCH + if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'i: kotlin.Int declared in .test5.' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=2 + then: ERROR_EXPR 'Loop not found for break expression: break' type=kotlin.Nothing + CALL 'public final fun forEach (action: kotlin.Function1): kotlin.Unit [inline] declared in kotlin.collections.CollectionsKt' type=kotlin.Unit origin=null + : kotlin.Int + $receiver: CALL 'public final fun listOf (vararg elements: T of kotlin.collections.CollectionsKt.listOf): kotlin.collections.List declared in kotlin.collections.CollectionsKt' type=kotlin.collections.List origin=null + : kotlin.Int + elements: VARARG type=kotlin.Array varargElementType=kotlin.Int + CONST Int type=kotlin.Int value=1 + CONST Int type=kotlin.Int value=2 + CONST Int type=kotlin.Int value=3 + action: FUN_EXPR type=kotlin.Function1 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> (i:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:i index:0 type:kotlin.Int + BLOCK_BODY + WHEN type=kotlin.Unit origin=IF + BRANCH + if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'i: kotlin.Int declared in .test5.' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=2 + then: ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + FUN name:test6 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 bar (block1: kotlin.Function0, block2: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block1: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.Unit declared in .test6' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + block2: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: 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 bar (block1: kotlin.Function0, block2: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block1: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.Unit declared in .test6' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + block2: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: 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 bar (block1: kotlin.Function0, block2: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block1: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + block2: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: 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 bar (block1: kotlin.Function0, block2: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block1: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + block2: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue' type=kotlin.Nothing + FUN name:test7 visibility:public modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + CALL 'public final fun myForEach (action: kotlin.Function1.myForEach, kotlin.Unit>): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + : kotlin.Int + $receiver: CALL 'public final fun rangeTo (other: kotlin.Int): kotlin.ranges.IntRange [operator] declared in kotlin.Int' type=kotlin.ranges.IntRange origin=RANGE + $this: CONST Int type=kotlin.Int value=1 + other: CONST Int type=kotlin.Int value=3 + action: FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (i:kotlin.Int) returnType:kotlin.Unit + VALUE_PARAMETER name:i index:0 type:kotlin.Int + BLOCK_BODY + WHEN type=kotlin.Unit origin=IF + BRANCH + if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'i: kotlin.Int declared in .test7.' type=kotlin.Int origin=null + 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 diff --git a/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt b/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt new file mode 100644 index 00000000000..243e2f28b2b --- /dev/null +++ b/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt @@ -0,0 +1,111 @@ +// LANGUAGE: +BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_FIR: JVM_IR +// !IGNORE_ERRORS +// WITH_STDLIB + +inline fun foo(block: () -> Unit) { block() } + +inline fun bar(block1: () -> Unit, noinline block2: () -> Unit) { + block1() + block2() +} + +inline fun Iterable.myForEach(action: (T) -> Unit): Unit { + for (element in this) action(element) +} + +fun test1() { + {break}() + {continue}() + + (fun () {break})() + (fun () {continue})() + + foo {break} + foo {continue} + + foo(fun () {break}) + foo(fun () {continue}) +} + +fun test2() { + L1@ while (true) { + {break@ERROR}() + {continue@ERROR}() + + (fun () {break@ERROR})() + (fun () {continue@ERROR})() + + foo {break@ERROR} + foo {continue@ERROR} + + foo(fun () {break@ERROR}) + foo(fun () {continue@ERROR}) + } +} + +fun test3() { + L1@ while (true) { + val lambda = { + {break@L1}() + {continue@L1}() + + (fun () {break@L1})() + (fun () {continue@L1})() + + foo {break@L1} + foo {continue@L1} + + foo(fun () {break@L1}) + foo(fun () {continue@L1}) + } + } +} + +fun test4() { + while ({ break }()) { + } + while ({ continue }()) { + } + + while ((fun() { break })()) { + } + while ((fun() { continue })()) { + } + + while (foo { break }) { + } + while (foo { continue }) { + } + + while (foo(fun() { break })) { + } + while (foo(fun() { continue })) { + } +} + +fun test5() { + listOf(1, 2, 3).forEach { i -> if (i == 2) break } + listOf(1, 2, 3).forEach { i -> if (i == 2) continue } + listOf(1, 2, 3).forEach(fun (i: Int) { if (i == 2) break }) + listOf(1, 2, 3).forEach(fun (i: Int) { if (i == 2) continue }) +} + +fun test6() { + while (true) { + bar({}, {break}) + bar({}, {continue}) + + bar(fun () {}, fun () {break}) + bar(fun () {}, fun () {continue}) + } +} + +fun test7() { + (1..3).myForEach { i -> + if (i == 2) { + break + } + } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt.txt b/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt.txt new file mode 100644 index 00000000000..d90a47bca5c --- /dev/null +++ b/compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt.txt @@ -0,0 +1,222 @@ +inline fun foo(block: Function0) { + block.invoke() +} + +inline fun bar(block1: Function0, noinline block2: Function0) { + block1.invoke() + block2.invoke() +} + +inline fun Iterable.myForEach(action: Function1) { + { // BLOCK + val tmp0_iterator: Iterator = .iterator() + while (tmp0_iterator.hasNext()) { // BLOCK + val element: T = tmp0_iterator.next() + action.invoke(p1 = element) + } + } +} + +fun test1() { + error("") /* ErrorExpression */ + local fun () { + error("") /* ErrorExpression */ + } +.invoke() + local fun () { + error("") /* ErrorExpression */ + } +.invoke() + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) +} + +fun test2() { + L1@ while (true) { // BLOCK + error("") /* ErrorExpression */ + local fun () { + error("") /* ErrorExpression */ + } +.invoke() + local fun () { + error("") /* ErrorExpression */ + } +.invoke() + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + } +} + +fun test3() { + L1@ while (true) { // BLOCK + val lambda: Function0 = local fun () { + error("") /* ErrorExpression */ + local fun () { + error("") /* ErrorExpression */ + } +.invoke() + local fun () { + error("") /* ErrorExpression */ + } +.invoke() + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + } + + } +} + +fun test4() { + while (local fun (): Nothing { + return error("") /* ErrorExpression */ + } +.invoke()) { // BLOCK + } + while (local fun (): Nothing { + return error("") /* ErrorExpression */ + } +.invoke()) { // BLOCK + } + while (local fun () { + error("") /* ErrorExpression */ + } +.invoke() /*as Boolean */) { // BLOCK + } + while (local fun () { + error("") /* ErrorExpression */ + } +.invoke() /*as Boolean */) { // BLOCK + } + while (foo(block = local fun () { + error("") /* ErrorExpression */ + } +) /*as Boolean */) { // BLOCK + } + while (foo(block = local fun () { + error("") /* ErrorExpression */ + } +) /*as Boolean */) { // BLOCK + } + while (foo(block = local fun () { + error("") /* ErrorExpression */ + } +) /*as Boolean */) { // BLOCK + } + while (foo(block = local fun () { + error("") /* ErrorExpression */ + } +) /*as Boolean */) { // BLOCK + } +} + +fun test5() { + listOf(elements = [1, 2, 3]).forEach(action = local fun (i: Int) { + when { + EQEQ(arg0 = i, arg1 = 2) -> error("") /* ErrorExpression */ + } + } +) + listOf(elements = [1, 2, 3]).forEach(action = local fun (i: Int) { + when { + EQEQ(arg0 = i, arg1 = 2) -> error("") /* ErrorExpression */ + } + } +) + listOf(elements = [1, 2, 3]).forEach(action = local fun (i: Int) { + when { + EQEQ(arg0 = i, arg1 = 2) -> error("") /* ErrorExpression */ + } + } +) + listOf(elements = [1, 2, 3]).forEach(action = local fun (i: Int) { + when { + EQEQ(arg0 = i, arg1 = 2) -> error("") /* ErrorExpression */ + } + } +) +} + +fun test6() { + while (true) { // BLOCK + bar(block1 = local fun () { + return Unit + } +, block2 = local fun () { + error("") /* ErrorExpression */ + } +) + bar(block1 = local fun () { + return Unit + } +, block2 = local fun () { + error("") /* ErrorExpression */ + } +) + bar(block1 = local fun () { + } +, block2 = local fun () { + error("") /* ErrorExpression */ + } +) + bar(block1 = local fun () { + } +, block2 = local fun () { + error("") /* ErrorExpression */ + } +) + } +} + +fun test7() { + 1.rangeTo(other = 3).myForEach(action = local fun (i: Int) { + when { + EQEQ(arg0 = i, arg1 = 2) -> { // BLOCK + error("") /* ErrorExpression */ + } + } + } +) +} + diff --git a/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.ir.txt b/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.ir.txt new file mode 100644 index 00000000000..970a153dee7 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.ir.txt @@ -0,0 +1,32 @@ +FILE fqName: fileName:/breakContinueInNoInlineLambda.kt + FUN name:foo visibility:public modality:FINAL <> (block:kotlin.Function0) returnType:kotlin.Unit [inline] + VALUE_PARAMETER name:block index:0 type:kotlin.Function0 [noinline] + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' + 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 [noinline] declared in .foo' type=kotlin.Function0 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: 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 [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + ERROR_EXPR 'Loop not found for continue expression: continue@L1' type=kotlin.Nothing diff --git a/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt b/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt new file mode 100644 index 00000000000..194dda9f371 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt @@ -0,0 +1,20 @@ +// 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 } + } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt.txt b/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt.txt new file mode 100644 index 00000000000..551199a55dc --- /dev/null +++ b/compiler/testData/ir/irText/expressions/breakContinueInNoInlineLambda.kt.txt @@ -0,0 +1,24 @@ +inline fun foo(noinline block: Function0) { + return block.invoke() +} + +fun test1() { + L1@ while (true) { // BLOCK + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + foo(block = local fun () { + error("") /* ErrorExpression */ + } +) + } +} diff --git a/compiler/testData/ir/irText/expressions/inlinedBreakContinue.ir.txt b/compiler/testData/ir/irText/expressions/inlinedBreakContinue.ir.txt new file mode 100644 index 00000000000..e56793bcc45 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/inlinedBreakContinue.ir.txt @@ -0,0 +1,80 @@ +FILE fqName: fileName:/inlinedBreakContinue.kt + FUN name:foo visibility:public modality:FINAL <> (block:kotlin.Function0) returnType:kotlin.Unit [inline] + VALUE_PARAMETER name:block index:0 type:kotlin.Function0 + 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 declared in .foo' type=kotlin.Function0 origin=VARIABLE_AS_FUNCTION + FUN name:bar visibility:public modality:FINAL <> (block1:kotlin.Function0, block2:kotlin.Function0) returnType:kotlin.Unit [inline] + VALUE_PARAMETER name:block1 index:0 type:kotlin.Function0 + VALUE_PARAMETER name:block2 index:1 type:kotlin.Function0 [noinline] + BLOCK_BODY + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=INVOKE + $this: GET_VAR 'block1: kotlin.Function0 declared in .bar' type=kotlin.Function0 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 [noinline] declared in .bar' type=kotlin.Function0 origin=VARIABLE_AS_FUNCTION + FUN name:test1 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 foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + BREAK label=null loop.label=null + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + CONTINUE label=null loop.label=null + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + BREAK label=null loop.label=null + CALL 'public final fun foo (block: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + CONTINUE label=null loop.label=null + FUN name:test2 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 bar (block1: kotlin.Function0, block2: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block1: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + BREAK label=null loop.label=null + block2: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.Unit declared in .test2' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + CALL 'public final fun bar (block1: kotlin.Function0, block2: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block1: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + CONTINUE label=null loop.label=null + block2: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.Unit declared in .test2' + GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit + CALL 'public final fun bar (block1: kotlin.Function0, block2: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block1: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + BREAK label=null loop.label=null + block2: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + CALL 'public final fun bar (block1: kotlin.Function0, block2: kotlin.Function0): kotlin.Unit [inline] declared in ' type=kotlin.Unit origin=null + block1: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY + CONTINUE label=null loop.label=null + block2: FUN_EXPR type=kotlin.Function0 origin=ANONYMOUS_FUNCTION + FUN LOCAL_FUNCTION name: visibility:local modality:FINAL <> () returnType:kotlin.Unit + BLOCK_BODY diff --git a/compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt b/compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt new file mode 100644 index 00000000000..63d9a2a5d83 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt @@ -0,0 +1,31 @@ +// LANGUAGE: +BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_FIR: JVM_IR +// !IGNORE_ERRORS +// WITH_STDLIB + +inline fun foo(block: () -> Unit) { block() } + +inline fun bar(block1: () -> Unit, noinline block2: () -> Unit) { + block1() + block2() +} + +fun test1() { + while (true) { + foo { break } + foo { continue } + foo(fun () { break }) + foo(fun () { continue }) + } +} + +fun test2() { + while (true) { + bar({break}, {}) + bar({continue}, {}) + + bar(fun () {break}, fun () {}) + bar(fun () {continue}, fun () {}) + } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt.txt b/compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt.txt new file mode 100644 index 00000000000..6357dc39b8e --- /dev/null +++ b/compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt.txt @@ -0,0 +1,60 @@ +inline fun foo(block: Function0) { + block.invoke() +} + +inline fun bar(block1: Function0, noinline block2: Function0) { + block1.invoke() + block2.invoke() +} + +fun test1() { + while (true) { // BLOCK + foo(block = local fun () { + break + } +) + foo(block = local fun () { + continue + } +) + foo(block = local fun () { + break + } +) + foo(block = local fun () { + continue + } +) + } +} + +fun test2() { + while (true) { // BLOCK + bar(block1 = local fun () { + break + } +, block2 = local fun () { + return Unit + } +) + bar(block1 = local fun () { + continue + } +, block2 = local fun () { + return Unit + } +) + bar(block1 = local fun () { + break + } +, block2 = local fun () { + } +) + bar(block1 = local fun () { + continue + } +, block2 = local fun () { + } +) + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 079f0304e85..18e4f007241 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -5482,6 +5482,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.kt"); } + @Test + @TestMetadata("breakContinueNoinline.kt") + public void testBreakContinueNoinline() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueNoinline.kt"); + } + + @Test + @TestMetadata("breakInLambdaPassedToDirectInvoke.kt") + public void testBreakInLambdaPassedToDirectInvoke() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInLambdaPassedToDirectInvoke.kt"); + } + @Test @TestMetadata("breakInsideLocal.kt") public void testBreakInsideLocal() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 727da069d78..33e569fd1c9 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -8913,6 +8913,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testWhileTrueBreak() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/whileTrueBreak.kt"); } + + @Nested + @TestMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue") + @TestDataPath("$PROJECT_ROOT") + public class InlinedBreakContinue { + @Test + public void testAllFilesPresentInInlinedBreakContinue() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + } } @Nested diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index c0b8b924f8a..9ceb4de8d5c 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -9033,6 +9033,52 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes public void testWhileTrueBreak() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/whileTrueBreak.kt"); } + + @Nested + @TestMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue") + @TestDataPath("$PROJECT_ROOT") + public class InlinedBreakContinue { + @Test + public void testAllFilesPresentInInlinedBreakContinue() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("inlineFunctionWithMultipleParameters.kt") + public void testInlineFunctionWithMultipleParameters() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt"); + } + + @Test + @TestMetadata("lambdaPassedToInlineFunction.kt") + public void testLambdaPassedToInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt"); + } + + @Test + @TestMetadata("loopWithinInlineFunction.kt") + public void testLoopWithinInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt"); + } + + @Test + @TestMetadata("stdlibFunctions.kt") + public void testStdlibFunctions() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt"); + } + + @Test + @TestMetadata("withReturnValue.kt") + public void testWithReturnValue() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt"); + } + } } @Nested diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ir/IrTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ir/IrTextTestGenerated.java index 58662c26682..9c997531737 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ir/IrTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ir/IrTextTestGenerated.java @@ -1196,6 +1196,12 @@ public class IrTextTestGenerated extends AbstractIrTextTest { runTest("compiler/testData/ir/irText/expressions/badBreakContinue.kt"); } + @Test + @TestMetadata("badInlinedBreakContinue.kt") + public void testBadInlinedBreakContinue() throws Exception { + runTest("compiler/testData/ir/irText/expressions/badInlinedBreakContinue.kt"); + } + @Test @TestMetadata("bangbang.kt") public void testBangbang() throws Exception { @@ -1238,6 +1244,12 @@ 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 { @@ -1502,6 +1514,12 @@ public class IrTextTestGenerated extends AbstractIrTextTest { runTest("compiler/testData/ir/irText/expressions/incrementDecrement.kt"); } + @Test + @TestMetadata("inlinedBreakContinue.kt") + public void testInlinedBreakContinue() throws Exception { + runTest("compiler/testData/ir/irText/expressions/inlinedBreakContinue.kt"); + } + @Test @TestMetadata("interfaceThisRef.kt") public void testInterfaceThisRef() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index bcaa380542d..c3524f6cdc3 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -6892,6 +6892,19 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes public void testWhileTrueBreak() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/whileTrueBreak.kt"); } + + @TestMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class InlinedBreakContinue extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInInlinedBreakContinue() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + } } @TestMetadata("compiler/testData/codegen/box/controlStructures/forInArray") diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.fir.kt index 1d16e30a439..3c61b57a60a 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.fir.kt @@ -1,3 +1,4 @@ +// LANGUAGE: -BreakContinueInInlineLambdas // !OPT_IN: kotlin.contracts.ExperimentalContracts // SKIP_TXT diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index ee680ed9d54..2f742765408 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -282,6 +282,7 @@ enum class LanguageFeature( ForbidInferringPostponedTypeVariableIntoDeclaredUpperBound(KOTLIN_1_9, kind = BUG_FIX), // KT-47986 SkipStandaloneScriptsInSourceRoots(KOTLIN_1_9, kind = OTHER), // KT-52525 ModifierNonBuiltinSuspendFunError(KOTLIN_1_9), + BreakContinueInInlineLambdas(KOTLIN_1_9, defaultState = State.ENABLED), // KT-1436 // Disabled for indefinite time. See KT-48535 and related discussion ApproximateIntegerLiteralTypesInReceiverPosition(sinceVersion = null), diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index 59375926e4f..21138a8db92 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java @@ -6077,6 +6077,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { public void testWhileTrueBreak() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/whileTrueBreak.kt"); } + + @Nested + @TestMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue") + @TestDataPath("$PROJECT_ROOT") + public class InlinedBreakContinue { + @Test + public void testAllFilesPresentInInlinedBreakContinue() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index 453fad2b479..b9760194901 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java @@ -6125,6 +6125,52 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { public void testWhileTrueBreak() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/whileTrueBreak.kt"); } + + @Nested + @TestMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue") + @TestDataPath("$PROJECT_ROOT") + public class InlinedBreakContinue { + @Test + public void testAllFilesPresentInInlinedBreakContinue() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + + @Test + @TestMetadata("inlineFunctionWithMultipleParameters.kt") + public void testInlineFunctionWithMultipleParameters() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt"); + } + + @Test + @TestMetadata("lambdaPassedToInlineFunction.kt") + public void testLambdaPassedToInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt"); + } + + @Test + @TestMetadata("loopWithinInlineFunction.kt") + public void testLoopWithinInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt"); + } + + @Test + @TestMetadata("stdlibFunctions.kt") + public void testStdlibFunctions() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt"); + } + + @Test + @TestMetadata("withReturnValue.kt") + public void testWithReturnValue() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt"); + } + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 805610df799..a9ab18f24d5 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -5392,6 +5392,49 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest public void testWhileTrueBreak() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/whileTrueBreak.kt"); } + + @TestMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class InlinedBreakContinue extends AbstractIrCodegenBoxWasmTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath); + } + + public void testAllFilesPresentInInlinedBreakContinue() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true); + } + + @TestMetadata("inlineFunctionWithMultipleParameters.kt") + public void testInlineFunctionWithMultipleParameters() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt"); + } + + @TestMetadata("lambdaPassedToInlineFunction.kt") + public void testLambdaPassedToInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt"); + } + + @TestMetadata("loopWithinInlineFunction.kt") + public void testLoopWithinInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt"); + } + + @TestMetadata("stdlibFunctions.kt") + public void testStdlibFunctions() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt"); + } + + @TestMetadata("withReturnValue.kt") + public void testWithReturnValue() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt"); + } + } } @TestMetadata("compiler/testData/codegen/box/controlStructures/forInArray") diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java index 13458bc2a92..c5aabed02da 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java @@ -6965,6 +6965,54 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest public void testWhileTrueBreak() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/whileTrueBreak.kt"); } + + @Nested + @TestMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue") + @TestDataPath("$PROJECT_ROOT") + @Tag("codegen") + @UseExtTestCaseGroupProvider() + public class InlinedBreakContinue { + @Test + public void testAllFilesPresentInInlinedBreakContinue() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); + } + + @Test + @TestMetadata("inlineFunctionWithMultipleParameters.kt") + public void testInlineFunctionWithMultipleParameters() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/inlineFunctionWithMultipleParameters.kt"); + } + + @Test + @TestMetadata("lambdaPassedToInlineFunction.kt") + public void testLambdaPassedToInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/lambdaPassedToInlineFunction.kt"); + } + + @Test + @TestMetadata("loopWithinInlineFunction.kt") + public void testLoopWithinInlineFunction() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/loopWithinInlineFunction.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/simple.kt"); + } + + @Test + @TestMetadata("stdlibFunctions.kt") + public void testStdlibFunctions() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/stdlibFunctions.kt"); + } + + @Test + @TestMetadata("withReturnValue.kt") + public void testWithReturnValue() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/inlinedBreakContinue/withReturnValue.kt"); + } + } } @Nested