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 86c64d92cf8..5ca8020e805 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 @@ -5626,6 +5626,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.kt"); } + @Test + @TestMetadata("inlinedBreakContinueFeatureDisabled.kt") + public void testInlinedBreakContinueFeatureDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt"); + } + @Test @TestMetadata("kt1001.kt") public void testKt1001() 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 d18e89b3b9b..036e6864ce3 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 @@ -5626,6 +5626,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.kt"); } + @Test + @TestMetadata("inlinedBreakContinueFeatureDisabled.kt") + public void testInlinedBreakContinueFeatureDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt"); + } + @Test @TestMetadata("kt1001.kt") public void testKt1001() 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 72eefa667c8..1bcf440a474 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 @@ -5626,6 +5626,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.kt"); } + @Test + @TestMetadata("inlinedBreakContinueFeatureDisabled.kt") + public void testInlinedBreakContinueFeatureDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt"); + } + @Test @TestMetadata("kt1001.kt") public void testKt1001() 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 40ca7c0cfd7..82849927c0c 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 @@ -14,8 +14,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors 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) { @@ -36,8 +34,19 @@ object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker when (element) { expression -> { - if (errorPathElements.any()) { - reporter.reportOn(expression.source, FirErrors.BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, context) + if (errorPathElements.isNotEmpty()) { + val hasNonInline = errorPathElements.any { + when(it) { + is FirAnonymousFunction -> it.inlineStatus != InlineStatus.Inline + is FirAnonymousFunctionExpression -> it.anonymousFunction.inlineStatus != InlineStatus.Inline + else -> true + }} + if (hasNonInline) { + reporter.reportOn(expression.source, FirErrors.BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, context) + } else if (!allowInlined) { + reporter.reportOn(expression.source, FirErrors.UNSUPPORTED_FEATURE, + LanguageFeature.BreakContinueInInlineLambdas to context.languageVersionSettings, context) + } } return true } @@ -63,17 +72,7 @@ object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker 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].run { isNoinline || isCrossinline } - ) argument.tryInline() else argument - if (findPathAndCheck(expressionToCheck)) { - return true - } - } + if (element.arguments.any(::findPathAndCheck)) return true } is FirCall -> { for (argument in element.arguments) { 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 ed015f875b8..cc5b93cb516 100644 --- a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -938,7 +938,13 @@ class ControlFlowProcessor( // See generateInitializersForClassOrObject && generateDeclarationForLocalClassOrObjectIfNeeded labelExprEnclosingFunc is ConstructorDescriptor && !labelExprEnclosingFunc.isPrimary ) { - trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression)) + val dependsOnInlineLambdas = !skipInlineFunctions && + getEnclosingFunctionDescriptor(bindingContext, jumpExpression, true) == getEnclosingFunctionDescriptor(bindingContext, jumpTarget, true) + if (dependsOnInlineLambdas) { + trace.report(UNSUPPORTED_FEATURE.on(jumpExpression, BreakContinueInInlineLambdas to languageVersionSettings)) + } else { + trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression)) + } } false } else { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt new file mode 100644 index 00000000000..28e81059ef6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt @@ -0,0 +1,39 @@ +// FIR_IDENTICAL +// LANGUAGE: -BreakContinueInInlineLambdas +// TARGET_BACKEND: JVM_IR +// IGNORE_ERRORS +// WITH_STDLIB + +inline fun foo(block: () -> Unit) { block() } + +inline fun bar(block1: () -> Unit, noinline block2: () -> Unit, crossinline block3: () -> Unit = {}) { + block1() + block2() + block3() +} + +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 () {}) + } +} + +fun test3() { + while (true) { + bar({}, { break }, { break }) + bar({}, { continue }, { break }) + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.txt new file mode 100644 index 00000000000..3d91ae6288f --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.txt @@ -0,0 +1,8 @@ +package + +public inline fun bar(/*0*/ block1: () -> kotlin.Unit, /*1*/ noinline block2: () -> kotlin.Unit, /*2*/ crossinline block3: () -> kotlin.Unit = ...): kotlin.Unit +public inline fun foo(/*0*/ block: () -> kotlin.Unit): kotlin.Unit +public fun test1(): kotlin.Unit +public fun test2(): kotlin.Unit +public fun test3(): kotlin.Unit + 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 0615dbdfa60..6669f71a3ee 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 @@ -5632,6 +5632,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.kt"); } + @Test + @TestMetadata("inlinedBreakContinueFeatureDisabled.kt") + public void testInlinedBreakContinueFeatureDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt"); + } + @Test @TestMetadata("kt1001.kt") public void testKt1001() throws Exception { 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 3c61b57a60a..a9d8b209e5b 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 @@ -6,14 +6,14 @@ fun case_1(value_1: Boolean) { while (value_1) { funWithExactlyOnceCallsInPlace { - break + break } println("1") } loop@ for (i in 0..10) { funWithExactlyOnceCallsInPlace { - break@loop + break@loop } println("1") } @@ -23,14 +23,14 @@ fun case_1(value_1: Boolean) { fun case_2(value_1: Boolean) { for (i in 0..10) { funWithExactlyOnceCallsInPlace { - continue + continue } println("1") } loop@ while (value_1) { funWithExactlyOnceCallsInPlace { - continue@loop + continue@loop } println("1") } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.kt index 80cc42e16c9..d9e8d210d20 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.kt @@ -14,14 +14,14 @@ fun case_1(value_1: Boolean) { while (value_1) { funWithExactlyOnceCallsInPlace { - break + break } println("1") } loop@ for (i in 0..10) { funWithExactlyOnceCallsInPlace { - break@loop + break@loop } println("1") } @@ -31,14 +31,14 @@ fun case_1(value_1: Boolean) { fun case_2(value_1: Boolean) { for (i in 0..10) { funWithExactlyOnceCallsInPlace { - continue + continue } println("1") } loop@ while (value_1) { funWithExactlyOnceCallsInPlace { - continue@loop + continue@loop } println("1") } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt index 0550df6d443..0e7283bba90 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.fir.checkers import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.config.KotlinCompilerVersion import org.jetbrains.kotlin.diagnostics.* +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker import org.jetbrains.kotlin.fir.analysis.checkers.isInlineClass @@ -18,6 +19,7 @@ import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.* @@ -30,6 +32,7 @@ import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySeria import org.jetbrains.kotlinx.serialization.compiler.fir.services.findTypeSerializerOrContextUnchecked import org.jetbrains.kotlinx.serialization.compiler.fir.services.serializablePropertiesProvider import org.jetbrains.kotlinx.serialization.compiler.fir.services.versionReader +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations object FirSerializationPluginClassChecker : FirClassChecker() { private val JAVA_SERIALIZABLE_ID = ClassId.topLevel(FqName("java.io.Serializable"))