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 59847a43942..67dba8d9f27 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 @@ -30141,6 +30141,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/smartCasts/variables/capturedByMultipleLambdas.kt"); } + @Test + @TestMetadata("capturedByNested.kt") + public void testCapturedByNested() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.kt"); + } + @Test @TestMetadata("doWhileWithMiddleBreak.kt") public void testDoWhileWithMiddleBreak() 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 59394aeb9fb..8f162dde7fe 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 @@ -30141,6 +30141,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/smartCasts/variables/capturedByMultipleLambdas.kt"); } + @Test + @TestMetadata("capturedByNested.kt") + public void testCapturedByNested() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.kt"); + } + @Test @TestMetadata("doWhileWithMiddleBreak.kt") public void testDoWhileWithMiddleBreak() 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 d839e7d7d22..76ba3f4a97f 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 @@ -30141,6 +30141,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/smartCasts/variables/capturedByMultipleLambdas.kt"); } + @Test + @TestMetadata("capturedByNested.kt") + public void testCapturedByNested() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.kt"); + } + @Test @TestMetadata("doWhileWithMiddleBreak.kt") public void testDoWhileWithMiddleBreak() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirLocalVariableAssignmentAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirLocalVariableAssignmentAnalyzer.kt index 91d60dfad37..c7b6227db4f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirLocalVariableAssignmentAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirLocalVariableAssignmentAnalyzer.kt @@ -112,14 +112,15 @@ internal class FirLocalVariableAssignmentAnalyzer( } fun exitLocalFunction(function: FirFunction) { - val eventOccurrencesRange: EventOccurrencesRange? = (function as? FirAnonymousFunction)?.invocationKind concurrentlyAssignedLocalVariablesStack.removeLast() - when (eventOccurrencesRange) { + when ((function as? FirAnonymousFunction)?.invocationKind) { EventOccurrencesRange.UNKNOWN, null -> // The function may be stored and then called later, so any access to the variables it touches // is no longer smartcastable ever. assignedLocalVariablesByFunction[function.symbol]?.insideLocalFunction?.let { - concurrentlyAssignedLocalVariablesStack.last() += it + for (outerScope in concurrentlyAssignedLocalVariablesStack) { + outerScope += it + } } else -> {} // The function is only called inline; this is handled by CFG construction by visiting the function body. } @@ -358,8 +359,9 @@ internal class FirLocalVariableAssignmentAnalyzer( class MiniCfgData(var flow: MiniFlow?) { val variableDeclarations: ArrayDeque> = ArrayDeque(listOf(mutableMapOf())) val localFunctionToAssignedLocalVariables: MutableMap, AssignedLocalVariables> = mutableMapOf() + fun resolveLocalVariable(name: Name): FirProperty? { - return variableDeclarations.asReversed().firstNotNullOfOrNull { it[name] } + return variableDeclarations.lastOrNull { name in it }?.getValue(name) } } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.kt new file mode 100644 index 00000000000..cc713e13c9a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +import kotlin.contracts.* + +@Suppress("OPT_IN_USAGE_ERROR", "OPT_IN_USAGE_FUTURE_ERROR") +fun exactlyOnce(f: () -> Unit) { + contract { + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + f() +} + +fun test() { + var s: String? = "" + if (s != null) { + val block: () -> Unit + exactlyOnce { + block = { s = null } + } + block() + s.length + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.txt new file mode 100644 index 00000000000..d837c4679dd --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.txt @@ -0,0 +1,6 @@ +package + +@kotlin.Suppress(names = {"OPT_IN_USAGE_ERROR", "OPT_IN_USAGE_FUTURE_ERROR"}) public fun exactlyOnce(/*0*/ f: () -> kotlin.Unit): kotlin.Unit + CallsInPlace(f, EXACTLY_ONCE) + +public fun test(): 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 c8a629058f7..c438b7f364a 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 @@ -30231,6 +30231,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/smartCasts/variables/capturedByMultipleLambdas.kt"); } + @Test + @TestMetadata("capturedByNested.kt") + public void testCapturedByNested() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/variables/capturedByNested.kt"); + } + @Test @TestMetadata("doWhileWithMiddleBreak.kt") public void testDoWhileWithMiddleBreak() throws Exception {