From 0944e8fc36560e7f7064456755565f5f528673e8 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Wed, 1 Nov 2023 19:04:59 +0900 Subject: [PATCH] [Analysis API] Keep the collected 'FirTowerDataContext' mutable 'ContextCollector' is used for computing context of 'FirCodeFragment's. Code fragments themselves might contain additional smart cast operations that modify the context receiver stack. ^KT-63056 Fixed --- ...ceModuleCompilerFacilityTestGenerated.java | 6 ++++ ...ceModuleCompilerFacilityTestGenerated.java | 6 ++++ ...extensionReceiverSmartCasted.capturing.txt | 3 ++ .../extensionReceiverSmartCasted.fragment.kt | 2 ++ .../extensionReceiverSmartCasted.ir.txt | 16 +++++++++++ .../capturing/extensionReceiverSmartCasted.kt | 13 +++++++++ .../extensionReceiverSmartCasted.txt | 5 ++++ .../builder/FirTowerDataContextCollector.kt | 4 +-- .../level/api/fir/util/ContextCollector.kt | 2 +- .../CodeFragmentCapturingTestGenerated.java | 6 ++++ .../kotlin/fir/resolve/calls/FirReceivers.kt | 28 +++++++++---------- .../body/resolve/BodyResolveContext.kt | 5 +++- .../fir/declarations/ImplicitReceiverUtils.kt | 14 +++++----- .../PersistentImplicitReceiverStack.kt | 4 +-- 14 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.capturing.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.fragment.kt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.ir.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.txt diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerated.java index c600d2265db..13e98d34e06 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerated.java @@ -196,6 +196,12 @@ public class FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerate runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverPropertyLabeled.kt"); } + @Test + @TestMetadata("extensionReceiverSmartCasted.kt") + public void testExtensionReceiverSmartCasted() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt"); + } + @Test @TestMetadata("initializer.kt") public void testInitializer() throws Exception { diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java index efaca46fbc9..21bfc0e939d 100644 --- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java +++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compilerFacility/FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated.java @@ -196,6 +196,12 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverPropertyLabeled.kt"); } + @Test + @TestMetadata("extensionReceiverSmartCasted.kt") + public void testExtensionReceiverSmartCasted() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt"); + } + @Test @TestMetadata("initializer.kt") public void testInitializer() throws Exception { diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.capturing.txt new file mode 100644 index 00000000000..02efd095c3e --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.capturing.txt @@ -0,0 +1,3 @@ +ExtensionReceiver[name: apply; isMutated: false; displayText: this@apply] + apply@fun R|Foo|.(): R|kotlin/Unit| + R|Foo| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.fragment.kt new file mode 100644 index 00000000000..9a6847ad11d --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.fragment.kt @@ -0,0 +1,2 @@ +this as FooImpl +n \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.ir.txt new file mode 100644 index 00000000000..7b25d8f5478 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.ir.txt @@ -0,0 +1,16 @@ +MODULE_FRAGMENT + FILE fqName: fileName:fragment.kt + CLASS CLASS name:CodeFragment modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.CodeFragment + CONSTRUCTOR visibility:public <> () returnType:.CodeFragment [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + FUN name:run visibility:public modality:FINAL <> (p0:.Foo) returnType:kotlin.Int + VALUE_PARAMETER name:p0 index:0 type:.Foo + EXPRESSION_BODY + BLOCK type=kotlin.Int origin=null + TYPE_OP type=.FooImpl origin=CAST typeOperand=.FooImpl + GET_VAR 'p0: .Foo declared in .CodeFragment.run' type=.Foo origin=null + CALL 'public final fun (): kotlin.Int declared in .FooImpl' type=kotlin.Int origin=GET_PROPERTY + $this: TYPE_OP type=.FooImpl origin=IMPLICIT_CAST typeOperand=.FooImpl + GET_VAR 'p0: .Foo declared in .CodeFragment.run' type=.Foo origin=null diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt new file mode 100644 index 00000000000..4f1e8420602 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt @@ -0,0 +1,13 @@ +interface Foo + +class FooImpl : Foo { + val n: Int = 5 +} + +fun makeFoo(): Foo = FooImpl() + +fun main() { + makeFoo().apply { + Unit + } +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.txt new file mode 100644 index 00000000000..d0a6da7e227 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: Foo): int +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirTowerDataContextCollector.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirTowerDataContextCollector.kt index 11c5fe31ab0..a24f69efec0 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirTowerDataContextCollector.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirTowerDataContextCollector.kt @@ -51,7 +51,7 @@ internal class FirTowerDataContextAllElementsCollector : FirResolveContextCollec override fun addStatementContext(statement: FirStatement, context: BodyResolveContext) { val closestParentExpression = statement.psi?.closestParentExpressionWithSameContextOrSelf() ?: return // FIR body transform may alter the context if there are implicit receivers with smartcast - elementsToContext[closestParentExpression] = context.towerDataContext.createSnapshot() + elementsToContext[closestParentExpression] = context.towerDataContext.createSnapshot(keepMutable = false) } override fun addDeclarationContext(declaration: FirDeclaration, context: BodyResolveContext) { @@ -62,7 +62,7 @@ internal class FirTowerDataContextAllElementsCollector : FirResolveContextCollec // we do not collect contexts for such declarations if (psi is KtDelegatedSuperTypeEntry) return - elementsToContext[psi] = context.towerDataContext.createSnapshot() + elementsToContext[psi] = context.towerDataContext.createSnapshot(keepMutable = false) } override fun addClassHeaderContext(declaration: FirRegularClass, context: FirTowerDataContext) { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt index d958b0e8eab..1059c0285d7 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt @@ -264,7 +264,7 @@ private class ContextCollectorVisitor( } } - val towerDataContextSnapshot = context.towerDataContext.createSnapshot() + val towerDataContextSnapshot = context.towerDataContext.createSnapshot(keepMutable = true) for ((index, oldType) in oldReceiverTypes) { implicitReceiverStack.replaceReceiverType(index, oldType) diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java index 59908069fc4..97210e22b5c 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/CodeFragmentCapturingTestGenerated.java @@ -72,6 +72,12 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverPropertyLabeled.kt"); } + @Test + @TestMetadata("extensionReceiverSmartCasted.kt") + public void testExtensionReceiverSmartCasted() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt"); + } + @Test @TestMetadata("initializer.kt") public void testInitializer() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt index 9870a102848..d0290e8dc7c 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt @@ -176,7 +176,7 @@ sealed class ImplicitReceiverValue>( ) } - abstract fun createSnapshot(): ImplicitReceiverValue + abstract fun createSnapshot(keepMutable: Boolean): ImplicitReceiverValue } private fun receiverExpression( @@ -219,8 +219,8 @@ class ImplicitDispatchReceiverValue( useSiteSession, scopeSession ) - override fun createSnapshot(): ImplicitReceiverValue> { - return ImplicitDispatchReceiverValue(boundSymbol, type, useSiteSession, scopeSession, false) + override fun createSnapshot(keepMutable: Boolean): ImplicitReceiverValue> { + return ImplicitDispatchReceiverValue(boundSymbol, type, useSiteSession, scopeSession, keepMutable) } override val isContextReceiver: Boolean @@ -234,8 +234,8 @@ class ImplicitExtensionReceiverValue( scopeSession: ScopeSession, mutable: Boolean = true, ) : ImplicitReceiverValue>(boundSymbol, type, useSiteSession, scopeSession, mutable) { - override fun createSnapshot(): ImplicitReceiverValue> { - return ImplicitExtensionReceiverValue(boundSymbol, type, useSiteSession, scopeSession, false) + override fun createSnapshot(keepMutable: Boolean): ImplicitReceiverValue> { + return ImplicitExtensionReceiverValue(boundSymbol, type, useSiteSession, scopeSession, keepMutable) } override val isContextReceiver: Boolean @@ -250,8 +250,8 @@ class InaccessibleImplicitReceiverValue( scopeSession: ScopeSession, mutable: Boolean = true, ) : ImplicitReceiverValue>(boundSymbol, type, useSiteSession, scopeSession, mutable, inaccessibleReceiver = true) { - override fun createSnapshot(): ImplicitReceiverValue> { - return InaccessibleImplicitReceiverValue(boundSymbol, type, useSiteSession, scopeSession, false) + override fun createSnapshot(keepMutable: Boolean): ImplicitReceiverValue> { + return InaccessibleImplicitReceiverValue(boundSymbol, type, useSiteSession, scopeSession, keepMutable) } override val isContextReceiver: Boolean @@ -269,7 +269,7 @@ sealed class ContextReceiverValue>( ) : ImplicitReceiverValue( boundSymbol, type, useSiteSession, scopeSession, mutable, contextReceiverNumber, ) { - abstract override fun createSnapshot(): ContextReceiverValue + abstract override fun createSnapshot(keepMutable: Boolean): ContextReceiverValue override val isContextReceiver: Boolean get() = true @@ -286,8 +286,8 @@ class ContextReceiverValueForCallable( ) : ContextReceiverValue>( boundSymbol, type, labelName, useSiteSession, scopeSession, mutable, contextReceiverNumber ) { - override fun createSnapshot(): ContextReceiverValue> = - ContextReceiverValueForCallable(boundSymbol, type, labelName, useSiteSession, scopeSession, mutable = false, contextReceiverNumber) + override fun createSnapshot(keepMutable: Boolean): ContextReceiverValue> = + ContextReceiverValueForCallable(boundSymbol, type, labelName, useSiteSession, scopeSession, keepMutable, contextReceiverNumber) } class ContextReceiverValueForClass( @@ -301,8 +301,8 @@ class ContextReceiverValueForClass( ) : ContextReceiverValue>( boundSymbol, type, labelName, useSiteSession, scopeSession, mutable, contextReceiverNumber ) { - override fun createSnapshot(): ContextReceiverValue> = - ContextReceiverValueForClass(boundSymbol, type, labelName, useSiteSession, scopeSession, mutable = false, contextReceiverNumber) + override fun createSnapshot(keepMutable: Boolean): ContextReceiverValue> = + ContextReceiverValueForClass(boundSymbol, type, labelName, useSiteSession, scopeSession, keepMutable, contextReceiverNumber) } class ImplicitReceiverValueForScript( @@ -316,7 +316,7 @@ class ImplicitReceiverValueForScript( ) : ContextReceiverValue( boundSymbol, type, labelName, useSiteSession, scopeSession, mutable, contextReceiverNumber ) { - override fun createSnapshot(): ContextReceiverValue = - ImplicitReceiverValueForScript(boundSymbol, type, labelName, useSiteSession, scopeSession, mutable = false, contextReceiverNumber) + override fun createSnapshot(keepMutable: Boolean): ContextReceiverValue = + ImplicitReceiverValueForScript(boundSymbol, type, labelName, useSiteSession, scopeSession, keepMutable, contextReceiverNumber) } 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 ea675a55846..d41363be6f4 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 @@ -949,7 +949,10 @@ class BodyResolveContext( @OptIn(PrivateForInline::class) fun storeCallableReferenceContext(callableReferenceAccess: FirCallableReferenceAccess) { - specialTowerDataContexts.storeCallableReferenceContext(callableReferenceAccess, towerDataContext.createSnapshot()) + specialTowerDataContexts.storeCallableReferenceContext( + callableReferenceAccess, + towerDataContext.createSnapshot(keepMutable = false) + ) } @OptIn(PrivateForInline::class) diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/ImplicitReceiverUtils.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/ImplicitReceiverUtils.kt index 8f72fb53183..5cb876f8ecd 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/ImplicitReceiverUtils.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/ImplicitReceiverUtils.kt @@ -236,12 +236,12 @@ class FirTowerDataContext private constructor( ) } - fun createSnapshot(): FirTowerDataContext { + fun createSnapshot(keepMutable: Boolean): FirTowerDataContext { return FirTowerDataContext( - towerDataElements.map(FirTowerDataElement::createSnapshot).toPersistentList(), - implicitReceiverStack.createSnapshot(), + towerDataElements.map { it.createSnapshot(keepMutable) }.toPersistentList(), + implicitReceiverStack.createSnapshot(keepMutable), localScopes.toPersistentList(), - nonLocalTowerDataElements.map(FirTowerDataElement::createSnapshot).toPersistentList() + nonLocalTowerDataElements.map { it.createSnapshot(keepMutable) }.toPersistentList() ) } } @@ -254,11 +254,11 @@ class FirTowerDataElement( val isLocal: Boolean, val staticScopeOwnerSymbol: FirRegularClassSymbol? = null ) { - fun createSnapshot(): FirTowerDataElement = + fun createSnapshot(keepMutable: Boolean): FirTowerDataElement = FirTowerDataElement( scope, - implicitReceiver?.createSnapshot(), - contextReceiverGroup?.map { it.createSnapshot() }, + implicitReceiver?.createSnapshot(keepMutable), + contextReceiverGroup?.map { it.createSnapshot(keepMutable) }, isLocal, staticScopeOwnerSymbol ) diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/PersistentImplicitReceiverStack.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/PersistentImplicitReceiverStack.kt index bbf16d0deea..e3d7e7f3a02 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/PersistentImplicitReceiverStack.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/PersistentImplicitReceiverStack.kt @@ -110,9 +110,9 @@ class PersistentImplicitReceiverStack private constructor( stack[index].updateTypeFromSmartcast(type) } - fun createSnapshot(): PersistentImplicitReceiverStack { + fun createSnapshot(keepMutable: Boolean): PersistentImplicitReceiverStack { return PersistentImplicitReceiverStack( - stack.map { it.createSnapshot() }.toPersistentList(), + stack.map { it.createSnapshot(keepMutable) }.toPersistentList(), receiversPerLabel, indexesPerSymbol, originalTypes