From eae673233bbe54f5704c48177ad71c01a2c9e98f Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Thu, 2 Jun 2022 18:27:29 +0300 Subject: [PATCH] FIR: Rework FirRegularTowerDataContexts - Get rid of SPECIAL mode (just left REGULAR and class-related instead) - Clear naming - Restore contexts after lambda/callable reference are processed The test has been failing before this change because after callable reference is resolved, its tower data context has been left erroneously in the SPECIAL-related entry --- ...CompilerTestFE10TestdataTestGenerated.java | 6 +++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 +++ .../resolve/FirRegularTowerDataContexts.kt | 39 ++++++++--------- .../body/resolve/BodyResolveContext.kt | 43 ++++++++----------- ...tCastWithLambdaAndCallableReference.fir.kt | 21 +++++++++ ...smartCastWithLambdaAndCallableReference.kt | 21 +++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++ 8 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.fir.kt create mode 100644 compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.kt 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 36595dac596..a3fd8c29821 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 @@ -7164,6 +7164,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/dataFlow/IsExpression.kt"); } + @Test + @TestMetadata("smartCastWithLambdaAndCallableReference.kt") + public void testSmartCastWithLambdaAndCallableReference() throws Exception { + runTest("compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.kt"); + } + @Test @TestMetadata("WhenSubject.kt") public void testWhenSubject() 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 7273154eb5a..b9574b07e66 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 @@ -7164,6 +7164,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/dataFlow/IsExpression.kt"); } + @Test + @TestMetadata("smartCastWithLambdaAndCallableReference.kt") + public void testSmartCastWithLambdaAndCallableReference() throws Exception { + runTest("compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.kt"); + } + @Test @TestMetadata("WhenSubject.kt") public void testWhenSubject() 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 75fa79ae9e9..6f688e991b3 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 @@ -7164,6 +7164,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/dataFlow/IsExpression.kt"); } + @Test + @TestMetadata("smartCastWithLambdaAndCallableReference.kt") + public void testSmartCastWithLambdaAndCallableReference() throws Exception { + runTest("compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.kt"); + } + @Test @TestMetadata("WhenSubject.kt") public void testWhenSubject() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirRegularTowerDataContexts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirRegularTowerDataContexts.kt index d8f27c8fe39..2a3a29c0639 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirRegularTowerDataContexts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirRegularTowerDataContexts.kt @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope import java.util.* enum class FirTowerDataMode { - MEMBER_DECLARATION, + REGULAR, NESTED_CLASS, COMPANION_OBJECT, CONSTRUCTOR_HEADER, @@ -22,65 +22,64 @@ class FirRegularTowerDataContexts private constructor( private val modeMap: EnumMap, val primaryConstructorPureParametersScope: FirLocalScope?, val primaryConstructorAllParametersScope: FirLocalScope?, - val mode: FirTowerDataMode, + val activeMode: FirTowerDataMode, ) { constructor( - forMemberDeclarations: FirTowerDataContext, + regular: FirTowerDataContext, forNestedClasses: FirTowerDataContext? = null, forCompanionObject: FirTowerDataContext? = null, forConstructorHeaders: FirTowerDataContext? = null, forEnumEntries: FirTowerDataContext? = null, - forSpecial: FirTowerDataContext? = null, primaryConstructorPureParametersScope: FirLocalScope? = null, primaryConstructorAllParametersScope: FirLocalScope? = null, ) : this( - enumMap(forMemberDeclarations, forNestedClasses, forCompanionObject, forConstructorHeaders, forEnumEntries, forSpecial), + enumMap(regular, forNestedClasses, forCompanionObject, forConstructorHeaders, forEnumEntries), primaryConstructorPureParametersScope, primaryConstructorAllParametersScope, - FirTowerDataMode.MEMBER_DECLARATION + FirTowerDataMode.REGULAR ) val currentContext: FirTowerDataContext? - get() = modeMap[mode] + get() = modeMap[activeMode] - fun copy(newContext: FirTowerDataContext): FirRegularTowerDataContexts { + fun replaceCurrentlyActiveContext(newContext: FirTowerDataContext): FirRegularTowerDataContexts { val modeMap = EnumMap(FirTowerDataMode::class.java) modeMap.putAll(this.modeMap) - modeMap[mode] = newContext - return FirRegularTowerDataContexts(modeMap, primaryConstructorPureParametersScope, primaryConstructorAllParametersScope, mode) + modeMap[activeMode] = newContext + return FirRegularTowerDataContexts(modeMap, primaryConstructorPureParametersScope, primaryConstructorAllParametersScope, activeMode) } - fun copy(newMode: FirTowerDataMode): FirRegularTowerDataContexts { - if (newMode == mode) return this + fun replaceTowerDataMode(newMode: FirTowerDataMode): FirRegularTowerDataContexts { + if (newMode == activeMode) return this return FirRegularTowerDataContexts(modeMap, primaryConstructorPureParametersScope, primaryConstructorAllParametersScope, newMode) } - fun copyWithSpecial(newContext: FirTowerDataContext): FirRegularTowerDataContexts { + // Effectively equal to replaceTowerDataMode(REGULAR) + replaceCurrentlyActiveContext(newContext) + // But left just for sake of optimization + fun replaceAndSetActiveRegularContext(newContext: FirTowerDataContext): FirRegularTowerDataContexts { val modeMap = EnumMap(FirTowerDataMode::class.java) modeMap.putAll(this.modeMap) - modeMap[FirTowerDataMode.SPECIAL] = newContext + modeMap[FirTowerDataMode.REGULAR] = newContext return FirRegularTowerDataContexts( - modeMap, primaryConstructorPureParametersScope, primaryConstructorAllParametersScope, FirTowerDataMode.SPECIAL + modeMap, primaryConstructorPureParametersScope, primaryConstructorAllParametersScope, FirTowerDataMode.REGULAR ) } companion object { private fun enumMap( - forMemberDeclarations: FirTowerDataContext, + regular: FirTowerDataContext, forNestedClasses: FirTowerDataContext?, forCompanionObject: FirTowerDataContext?, forConstructorHeaders: FirTowerDataContext?, forEnumEntries: FirTowerDataContext?, - forSpecial: FirTowerDataContext?, ): EnumMap { val modeMap = EnumMap(FirTowerDataMode::class.java) - modeMap[FirTowerDataMode.MEMBER_DECLARATION] = forMemberDeclarations + modeMap[FirTowerDataMode.REGULAR] = regular modeMap[FirTowerDataMode.NESTED_CLASS] = forNestedClasses modeMap[FirTowerDataMode.COMPANION_OBJECT] = forCompanionObject modeMap[FirTowerDataMode.CONSTRUCTOR_HEADER] = forConstructorHeaders modeMap[FirTowerDataMode.ENUM_ENTRY] = forEnumEntries - modeMap[FirTowerDataMode.SPECIAL] = forSpecial return modeMap } } -} \ No newline at end of file +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt index e202dfb1ee0..1d165ef5277 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt @@ -52,7 +52,7 @@ class BodyResolveContext( lateinit var file: FirFile @PrivateForInline - var regularTowerDataContexts = FirRegularTowerDataContexts(forMemberDeclarations = FirTowerDataContext()) + var regularTowerDataContexts = FirRegularTowerDataContexts(regular = FirTowerDataContext()) @PrivateForInline val specialTowerDataContexts = FirSpecialTowerDataContexts() @@ -64,9 +64,9 @@ class BodyResolveContext( @OptIn(PrivateForInline::class) var towerDataMode: FirTowerDataMode - get() = regularTowerDataContexts.mode + get() = regularTowerDataContexts.activeMode set(value) { - regularTowerDataContexts = regularTowerDataContexts.copy(newMode = value) + regularTowerDataContexts = regularTowerDataContexts.replaceTowerDataMode(newMode = value) } val implicitReceiverStack: ImplicitReceiverStack @@ -95,7 +95,7 @@ class BodyResolveContext( get() = containingClassDeclarations.lastOrNull() @OptIn(PrivateForInline::class) - inline fun withNewTowerDataForClass(newContexts: FirRegularTowerDataContexts, f: () -> T): T { + inline fun withTowerDataContexts(newContexts: FirRegularTowerDataContexts, f: () -> T): T { val old = regularTowerDataContexts regularTowerDataContexts = newContexts return try { @@ -158,14 +158,14 @@ class BodyResolveContext( @PrivateForInline inline fun withTowerDataMode(mode: FirTowerDataMode, f: () -> T): T { - return withTowerModeCleanup { + return withTowerDataModeCleanup { towerDataMode = mode f() } } @PrivateForInline - inline fun withTowerModeCleanup(l: () -> R): R { + inline fun withTowerDataModeCleanup(l: () -> R): R { val initialMode = towerDataMode return try { l() @@ -176,7 +176,7 @@ class BodyResolveContext( @PrivateForInline fun replaceTowerDataContext(newContext: FirTowerDataContext) { - regularTowerDataContexts = regularTowerDataContexts.copy(newContext) + regularTowerDataContexts = regularTowerDataContexts.replaceCurrentlyActiveContext(newContext) } @PrivateForInline @@ -315,23 +315,19 @@ class BodyResolveContext( @OptIn(PrivateForInline::class) inline fun withAnonymousFunctionTowerDataContext(symbol: FirAnonymousFunctionSymbol, f: () -> T): T { - return withTowerModeCleanup { - val newContext = specialTowerDataContexts.getAnonymousFunctionContext(symbol) - if (newContext != null) { - regularTowerDataContexts = regularTowerDataContexts.copyWithSpecial(newContext) - } - f() - } + return withTemporaryRegularContext(specialTowerDataContexts.getAnonymousFunctionContext(symbol), f) } @OptIn(PrivateForInline::class) inline fun withCallableReferenceTowerDataContext(access: FirCallableReferenceAccess, f: () -> T): T { - return withTowerModeCleanup { - val newContext = specialTowerDataContexts.getCallableReferenceContext(access) - if (newContext != null) { - regularTowerDataContexts = regularTowerDataContexts.copyWithSpecial(newContext) - } - f() + return withTemporaryRegularContext(specialTowerDataContexts.getCallableReferenceContext(access), f) + } + + @PrivateForInline + inline fun withTemporaryRegularContext(newContext: FirTowerDataContext?, f: () -> T): T { + if (newContext == null) return f() + return withTowerDataModeCleanup { + withTowerDataContexts(regularTowerDataContexts.replaceAndSetActiveRegularContext(newContext), f) } } @@ -396,7 +392,7 @@ class BodyResolveContext( withContainerClass(regularClass, f) } } - return withTowerModeCleanup { + return withTowerDataModeCleanup { if (!regularClass.isInner && containerIfAny is FirRegularClass) { towerDataMode = if (regularClass.isCompanion) { FirTowerDataMode.COMPANION_OBJECT @@ -486,12 +482,11 @@ class BodyResolveContext( statics, scopeForConstructorHeader, scopeForEnumEntries, - forSpecial = null, primaryConstructorPureParametersScope, primaryConstructorAllParametersScope ) - return withNewTowerDataForClass(newContexts) { + return withTowerDataContexts(newContexts) { f() } } @@ -521,7 +516,7 @@ class BodyResolveContext( val whenSubjectImportingScope = whenSubjectImportingScopes.lastOrNull() ?: return f() val newTowerDataContext = towerDataContext.addNonLocalScope(whenSubjectImportingScope) val newContexts = FirRegularTowerDataContexts(newTowerDataContext) - return withNewTowerDataForClass(newContexts) { + return withTowerDataContexts(newContexts) { f() } } diff --git a/compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.fir.kt b/compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.fir.kt new file mode 100644 index 00000000000..abffd1ab05e --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.fir.kt @@ -0,0 +1,21 @@ +// SKIP_TXT + +fun T.myApply(block: T.() -> Unit): T = this + +fun bar(): Int = 1 + +interface A : C +interface B : C +interface C { + fun baz() +} + +fun Any.foo() = myApply { + when (this) { + is A -> ::bar + is B -> ::bar + else -> throw RuntimeException() + } + + baz() // Smart cast should work +} diff --git a/compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.kt b/compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.kt new file mode 100644 index 00000000000..de11649a84e --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.kt @@ -0,0 +1,21 @@ +// SKIP_TXT + +fun T.myApply(block: T.() -> Unit): T = this + +fun bar(): Int = 1 + +interface A : C +interface B : C +interface C { + fun baz() +} + +fun Any.foo() = myApply { + when (this) { + is A -> ::bar + is B -> ::bar + else -> throw RuntimeException() + } + + baz() // Smart cast should work +} 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 37c8767b1eb..516d8fd469d 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 @@ -7170,6 +7170,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/dataFlow/IsExpression.kt"); } + @Test + @TestMetadata("smartCastWithLambdaAndCallableReference.kt") + public void testSmartCastWithLambdaAndCallableReference() throws Exception { + runTest("compiler/testData/diagnostics/tests/dataFlow/smartCastWithLambdaAndCallableReference.kt"); + } + @Test @TestMetadata("WhenSubject.kt") public void testWhenSubject() throws Exception {