From 3d92d0d05fcebb7d760d81d3f17aac2a9a472c56 Mon Sep 17 00:00:00 2001 From: Alexey Merkulov Date: Thu, 27 Jul 2023 18:49:55 +0200 Subject: [PATCH] [LL API] Fix value capturing for multiple receivers There were problem when 'this' bound symbol was used twice for a call KT-61144 --- .../fir/components/KtFirCompilerFacility.kt | 26 +++++++++----- ...ceModuleCompilerFacilityTestGenerated.java | 18 ++++++++++ ...sAndFunctionContextReceivers.capturing.txt | 16 +++++++++ ...assAndFunctionContextReceivers.fragment.kt | 1 + ...pleClassAndFunctionContextReceivers.ir.txt | 21 +++++++++++ ...ultipleClassAndFunctionContextReceivers.kt | 16 +++++++++ ...ltipleClassAndFunctionContextReceivers.txt | 5 +++ ...ultipleClassContextReceivers.capturing.txt | 8 +++++ .../multipleClassContextReceivers.fragment.kt | 1 + .../multipleClassContextReceivers.ir.txt | 17 +++++++++ .../multipleClassContextReceivers.kt | 13 +++++++ .../multipleClassContextReceivers.txt | 5 +++ ...ipleFunctionContextReceivers.capturing.txt | 8 +++++ ...ltipleFunctionContextReceivers.fragment.kt | 1 + .../multipleFunctionContextReceivers.ir.txt | 15 ++++++++ .../multipleFunctionContextReceivers.kt | 11 ++++++ .../multipleFunctionContextReceivers.txt | 5 +++ .../CodeFragmentCapturedValueAnalyzer.kt | 36 +++++++++++-------- .../CodeFragmentCapturingTestGenerated.java | 18 ++++++++++ .../kotlin/fir/backend/CodeFragmentUtils.kt | 2 +- 20 files changed, 219 insertions(+), 24 deletions(-) create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.capturing.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.fragment.kt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.ir.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.kt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.capturing.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.fragment.kt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.ir.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.kt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.capturing.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.fragment.kt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.ir.txt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.kt create mode 100644 analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.txt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompilerFacility.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompilerFacility.kt index e3b811f4868..22bb8d1edbe 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompilerFacility.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompilerFacility.kt @@ -14,9 +14,13 @@ import org.jetbrains.kotlin.analysis.api.diagnostics.KtDiagnostic import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession import org.jetbrains.kotlin.analysis.api.impl.base.util.KtCompiledFileForOutputFile import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirInternals -import org.jetbrains.kotlin.analysis.low.level.api.fir.api.* +import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter +import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDiagnosticsForFile +import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.resolve +import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentCapturedId import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentCapturedValueAnalyzer import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CompilationPeerCollector import org.jetbrains.kotlin.analysis.low.level.api.fir.util.codeFragment @@ -324,7 +328,9 @@ internal class KtFirCompilerFacility( val capturedSymbols = capturedData.symbols val capturedValues = capturedSymbols.map { it.value } - val injectedSymbols = capturedSymbols.map { InjectedValue(it.symbol, it.typeRef, it.value.isMutated) } + val injectedSymbols = capturedSymbols.map { + InjectedValue(it.symbol, it.contextReceiverNumber, it.typeRef, it.value.isMutated) + } codeFragment.conversionData = CodeFragmentConversionData( classId = ClassId(FqName.ROOT, Name.identifier(configuration[CODE_FRAGMENT_CLASS_NAME] ?: "CodeFragment")), @@ -332,7 +338,9 @@ internal class KtFirCompilerFacility( injectedSymbols ) - val injectedSymbolMapping = injectedSymbols.associateBy { it.symbol } + val injectedSymbolMapping = injectedSymbols.associateBy { + CodeFragmentCapturedId(it.symbol, it.contextReceiverNumber) + } val injectedValueProvider = InjectedSymbolProvider(mainKtFile, injectedSymbolMapping) return CodeFragmentMappings(capturedValues, capturedData.files, injectedValueProvider) @@ -340,7 +348,7 @@ internal class KtFirCompilerFacility( private class InjectedSymbolProvider( private val mainKtFile: KtFile, - private val injectedSymbolMapping: Map, InjectedValue> + private val injectedSymbolMapping: Map ) : (FirReference, Fir2IrConversionScope) -> InjectedValue? { override fun invoke(calleeReference: FirReference, conversionScope: Fir2IrConversionScope): InjectedValue? { val irFile = conversionScope.containingFileIfAny() @@ -350,11 +358,13 @@ internal class KtFirCompilerFacility( return null } - val symbol = when (calleeReference) { - is FirThisReference -> calleeReference.boundSymbol - else -> calleeReference.toResolvedSymbol>() + val id = when (calleeReference) { + is FirThisReference -> calleeReference.boundSymbol?.let { + CodeFragmentCapturedId(it, calleeReference.contextReceiverNumber) + } + else -> calleeReference.toResolvedSymbol>()?.let { CodeFragmentCapturedId(it) } } - return injectedSymbolMapping[symbol] + return injectedSymbolMapping[id] } } 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 8f93f278bf4..1f50d4f3682 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 @@ -220,6 +220,24 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localMutated.kt"); } + @Test + @TestMetadata("multipleClassAndFunctionContextReceivers.kt") + public void testMultipleClassAndFunctionContextReceivers() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.kt"); + } + + @Test + @TestMetadata("multipleClassContextReceivers.kt") + public void testMultipleClassContextReceivers() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.kt"); + } + + @Test + @TestMetadata("multipleFunctionContextReceivers.kt") + public void testMultipleFunctionContextReceivers() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.kt"); + } + @Test @TestMetadata("nestedOuterClass.kt") public void testNestedOuterClass() throws Exception { diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.capturing.txt new file mode 100644 index 00000000000..d8e5e8e9dad --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.capturing.txt @@ -0,0 +1,16 @@ +ContainingClass[name: ; isMutated: false; displayText: this@Test] + context(R|Ctx1|, R|Ctx2|) + class Test : R|kotlin/Any| + R|Test| +ContainingClass[name: ; isMutated: false; displayText: this@Test] + context(R|Ctx1|, R|Ctx2|) + class Test : R|kotlin/Any| + R|Test| +ContextReceiver[name: Ctx3; isMutated: false; displayText: this@Ctx3] + context(R|Ctx3|, R|Ctx4|) + fun foo(): R|kotlin/Unit| + R|Ctx3| +ContextReceiver[name: Ctx4; isMutated: false; displayText: this@Ctx4] + context(R|Ctx3|, R|Ctx4|) + fun foo(): R|kotlin/Unit| + R|Ctx4| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.fragment.kt new file mode 100644 index 00000000000..3cd27954e98 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.fragment.kt @@ -0,0 +1 @@ +useWithCtx1Ctx2Ctx3Ctx4() diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.ir.txt new file mode 100644 index 00000000000..b754570b94a --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.ir.txt @@ -0,0 +1,21 @@ +MODULE_FRAGMENT name: + 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:.Test, p1:.Test, p2:.Ctx3, p3:.Ctx4) returnType:kotlin.Int + VALUE_PARAMETER name:p0 index:0 type:.Test + VALUE_PARAMETER name:p1 index:1 type:.Test + VALUE_PARAMETER name:p2 index:2 type:.Ctx3 + VALUE_PARAMETER name:p3 index:3 type:.Ctx4 + EXPRESSION_BODY + BLOCK type=kotlin.Int origin=null + CALL 'public final fun useWithCtx1Ctx2Ctx3Ctx4 ($context_receiver_0: .Ctx1, $context_receiver_1: .Ctx2, $context_receiver_2: .Ctx3, $context_receiver_3: .Ctx4): kotlin.Int declared in .MultipleClassAndFunctionContextReceiversKt' type=kotlin.Int origin=null + $context_receiver_0: GET_FIELD 'FIELD FIELD_FOR_CLASS_CONTEXT_RECEIVER name:contextReceiverField0 type:.Ctx1 visibility:private [final]' type=.Ctx1 origin=null + receiver: GET_VAR 'p0: .Test declared in .CodeFragment.run' type=.Test origin=null + $context_receiver_1: GET_FIELD 'FIELD FIELD_FOR_CLASS_CONTEXT_RECEIVER name:contextReceiverField1 type:.Ctx2 visibility:private [final]' type=.Ctx2 origin=null + receiver: GET_VAR 'p1: .Test declared in .CodeFragment.run' type=.Test origin=null + $context_receiver_2: GET_VAR 'p2: .Ctx3 declared in .CodeFragment.run' type=.Ctx3 origin=null + $context_receiver_3: GET_VAR 'p3: .Ctx4 declared in .CodeFragment.run' type=.Ctx4 origin=null diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.kt new file mode 100644 index 00000000000..6f137971595 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.kt @@ -0,0 +1,16 @@ +// !LANGUAGE: +ContextReceivers +class Ctx1 +class Ctx2 +class Ctx3 +class Ctx4 + +context(Ctx1, Ctx2, Ctx3, Ctx4) +fun useWithCtx1Ctx2Ctx3Ctx4() = 3 + +context(Ctx1, Ctx2) +class Test { + context(Ctx3, Ctx4) + fun foo() { + val x = 1 + } +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.txt new file mode 100644 index 00000000000..ad8eb5f99e8 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: Test, p1: Test, p2: Ctx3, p3: Ctx4): int +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.capturing.txt new file mode 100644 index 00000000000..ffb95bf2181 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.capturing.txt @@ -0,0 +1,8 @@ +ContainingClass[name: ; isMutated: false; displayText: this@Test] + context(R|Ctx1|, R|Ctx2|) + class Test : R|kotlin/Any| + R|Test| +ContainingClass[name: ; isMutated: false; displayText: this@Test] + context(R|Ctx1|, R|Ctx2|) + class Test : R|kotlin/Any| + R|Test| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.fragment.kt new file mode 100644 index 00000000000..f4279159c44 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.fragment.kt @@ -0,0 +1 @@ +useWithCtx1Ctx2() diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.ir.txt new file mode 100644 index 00000000000..39516e546e1 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.ir.txt @@ -0,0 +1,17 @@ +MODULE_FRAGMENT name: + 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:.Test, p1:.Test) returnType:kotlin.Int + VALUE_PARAMETER name:p0 index:0 type:.Test + VALUE_PARAMETER name:p1 index:1 type:.Test + EXPRESSION_BODY + BLOCK type=kotlin.Int origin=null + CALL 'public final fun useWithCtx1Ctx2 ($context_receiver_0: .Ctx1, $context_receiver_1: .Ctx2): kotlin.Int declared in .MultipleClassContextReceiversKt' type=kotlin.Int origin=null + $context_receiver_0: GET_FIELD 'FIELD FIELD_FOR_CLASS_CONTEXT_RECEIVER name:contextReceiverField0 type:.Ctx1 visibility:private [final]' type=.Ctx1 origin=null + receiver: GET_VAR 'p0: .Test declared in .CodeFragment.run' type=.Test origin=null + $context_receiver_1: GET_FIELD 'FIELD FIELD_FOR_CLASS_CONTEXT_RECEIVER name:contextReceiverField1 type:.Ctx2 visibility:private [final]' type=.Ctx2 origin=null + receiver: GET_VAR 'p1: .Test declared in .CodeFragment.run' type=.Test origin=null diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.kt new file mode 100644 index 00000000000..b1927cae5b9 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.kt @@ -0,0 +1,13 @@ +// !LANGUAGE: +ContextReceivers +class Ctx1 +class Ctx2 + +context(Ctx1, Ctx2) +fun useWithCtx1Ctx2() = 3 + +context(Ctx1, Ctx2) +class Test { + fun foo() { + val x = 1 + } +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.txt new file mode 100644 index 00000000000..98d0b8c2792 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: Test, p1: Test): int +} diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.capturing.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.capturing.txt new file mode 100644 index 00000000000..e4e02837dba --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.capturing.txt @@ -0,0 +1,8 @@ +ContextReceiver[name: Ctx1; isMutated: false; displayText: this@Ctx1] + context(R|Ctx1|, R|Ctx2|) + fun foo(): R|kotlin/Unit| + R|Ctx1| +ContextReceiver[name: Ctx2; isMutated: false; displayText: this@Ctx2] + context(R|Ctx1|, R|Ctx2|) + fun foo(): R|kotlin/Unit| + R|Ctx2| diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.fragment.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.fragment.kt new file mode 100644 index 00000000000..f4279159c44 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.fragment.kt @@ -0,0 +1 @@ +useWithCtx1Ctx2() diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.ir.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.ir.txt new file mode 100644 index 00000000000..5bfb81c8ae1 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.ir.txt @@ -0,0 +1,15 @@ +MODULE_FRAGMENT name: + 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:.Ctx1, p1:.Ctx2) returnType:kotlin.Int + VALUE_PARAMETER name:p0 index:0 type:.Ctx1 + VALUE_PARAMETER name:p1 index:1 type:.Ctx2 + EXPRESSION_BODY + BLOCK type=kotlin.Int origin=null + CALL 'public final fun useWithCtx1Ctx2 ($context_receiver_0: .Ctx1, $context_receiver_1: .Ctx2): kotlin.Int declared in .MultipleFunctionContextReceiversKt' type=kotlin.Int origin=null + $context_receiver_0: GET_VAR 'p0: .Ctx1 declared in .CodeFragment.run' type=.Ctx1 origin=null + $context_receiver_1: GET_VAR 'p1: .Ctx2 declared in .CodeFragment.run' type=.Ctx2 origin=null diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.kt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.kt new file mode 100644 index 00000000000..e7d6f35cda1 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.kt @@ -0,0 +1,11 @@ +// !LANGUAGE: +ContextReceivers +class Ctx1 +class Ctx2 + +context(Ctx1, Ctx2) +fun useWithCtx1Ctx2() = 3 + +context(Ctx1, Ctx2) +fun foo() { + val x = 1 +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.txt b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.txt new file mode 100644 index 00000000000..8a47844ede2 --- /dev/null +++ b/analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.txt @@ -0,0 +1,5 @@ +public final class CodeFragment { + // source: 'fragment.kt' + public method (): void + public final static method run(p0: Ctx1, p1: Ctx2): int +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentCapturedValueAnalyzer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentCapturedValueAnalyzer.kt index 71836cc3a80..5a80443635a 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentCapturedValueAnalyzer.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentCapturedValueAnalyzer.kt @@ -43,9 +43,12 @@ import kotlin.collections.LinkedHashMap class CodeFragmentCapturedSymbol( val value: CodeFragmentCapturedValue, val symbol: FirBasedSymbol<*>, - val typeRef: FirTypeRef + val typeRef: FirTypeRef, + val contextReceiverNumber: Int = -1 ) +data class CodeFragmentCapturedId(val symbol: FirBasedSymbol<*>, val contextReceiverNumber: Int = -1) + object CodeFragmentCapturedValueAnalyzer { fun analyze(resolveSession: LLFirResolveSession, codeFragment: FirCodeFragment): CodeFragmentCapturedValueData { val selfSymbols = CodeFragmentDeclarationCollector().apply { codeFragment.accept(this) }.symbols.toSet() @@ -76,7 +79,7 @@ private class CodeFragmentCapturedValueVisitor( private val resolveSession: LLFirResolveSession, private val selfSymbols: Set>, ) : FirDefaultVisitorVoid() { - private val collectedMappings = LinkedHashMap, CodeFragmentCapturedSymbol>() + private val collectedMappings = LinkedHashMap() private val collectedFiles = LinkedHashSet() private val assignmentLhs = mutableListOf>() @@ -112,10 +115,11 @@ private class CodeFragmentCapturedValueVisitor( if (symbol != null && symbol !in selfSymbols) { val isCrossingInlineBounds = isCrossingInlineBounds(element, symbol) val capturedValue = CodeFragmentCapturedValue.SuperClass(symbol.classId, isCrossingInlineBounds) - register(symbol, CodeFragmentCapturedSymbol(capturedValue, symbol, element.superTypeRef)) + register(CodeFragmentCapturedSymbol(capturedValue, symbol, element.superTypeRef)) } } is FirThisReference -> { + val contextReceiverNumber = element.contextReceiverNumber val symbol = element.boundSymbol if (symbol != null && symbol !in selfSymbols) { when (symbol) { @@ -124,11 +128,10 @@ private class CodeFragmentCapturedValueVisitor( val isCrossingInlineBounds = isCrossingInlineBounds(element, symbol) val capturedValue = CodeFragmentCapturedValue.ContainingClass(symbol.classId, isCrossingInlineBounds) val typeRef = buildResolvedTypeRef { type = symbol.defaultType() } - register(symbol, CodeFragmentCapturedSymbol(capturedValue, symbol, typeRef)) + register(CodeFragmentCapturedSymbol(capturedValue, symbol, typeRef, contextReceiverNumber)) } } is FirFunctionSymbol<*> -> { - val contextReceiverNumber = element.contextReceiverNumber if (contextReceiverNumber >= 0) { val contextReceiver = symbol.resolvedContextReceivers[contextReceiverNumber] val labelName = contextReceiver.labelName @@ -136,7 +139,9 @@ private class CodeFragmentCapturedValueVisitor( val isCrossingInlineBounds = isCrossingInlineBounds(element, symbol) val capturedValue = CodeFragmentCapturedValue .ContextReceiver(contextReceiverNumber, labelName, isCrossingInlineBounds) - register(symbol, CodeFragmentCapturedSymbol(capturedValue, symbol, contextReceiver.typeRef)) + register( + CodeFragmentCapturedSymbol(capturedValue, symbol, contextReceiver.typeRef, contextReceiverNumber) + ) } } else { val labelName = element.labelName @@ -146,7 +151,7 @@ private class CodeFragmentCapturedValueVisitor( val typeRef = symbol.receiverParameter?.typeRef ?: error("Receiver parameter not found") val isCrossingInlineBounds = isCrossingInlineBounds(element, symbol) val capturedValue = CodeFragmentCapturedValue.ExtensionReceiver(labelName, isCrossingInlineBounds) - register(symbol, CodeFragmentCapturedSymbol(capturedValue, symbol, typeRef)) + register(CodeFragmentCapturedSymbol(capturedValue, symbol, typeRef, contextReceiverNumber)) } } } @@ -172,7 +177,7 @@ private class CodeFragmentCapturedValueVisitor( is FirValueParameterSymbol -> { val isCrossingInlineBounds = isCrossingInlineBounds(element, symbol) val capturedValue = CodeFragmentCapturedValue.Local(symbol.name, symbol.isMutated, isCrossingInlineBounds) - register(symbol, CodeFragmentCapturedSymbol(capturedValue, symbol, symbol.resolvedReturnTypeRef)) + register(CodeFragmentCapturedSymbol(capturedValue, symbol, symbol.resolvedReturnTypeRef)) } is FirPropertySymbol -> { if (symbol.isLocal) { @@ -182,14 +187,14 @@ private class CodeFragmentCapturedValueVisitor( symbol.hasDelegate -> CodeFragmentCapturedValue.LocalDelegate(symbol.name, symbol.isMutated, isCrossingInlineBounds) else -> CodeFragmentCapturedValue.Local(symbol.name, symbol.isMutated, isCrossingInlineBounds) } - register(symbol, CodeFragmentCapturedSymbol(capturedValue, symbol, symbol.resolvedReturnTypeRef)) + register(CodeFragmentCapturedSymbol(capturedValue, symbol, symbol.resolvedReturnTypeRef)) } } is FirBackingFieldSymbol -> { val propertyName = symbol.propertySymbol.name val isCrossingInlineBounds = isCrossingInlineBounds(element, symbol) val capturedValue = CodeFragmentCapturedValue.BackingField(propertyName, symbol.isMutated, isCrossingInlineBounds) - register(symbol, CodeFragmentCapturedSymbol(capturedValue, symbol, symbol.resolvedReturnTypeRef)) + register(CodeFragmentCapturedSymbol(capturedValue, symbol, symbol.resolvedReturnTypeRef)) } is FirNamedFunctionSymbol -> { if (symbol.isLocal) { @@ -201,12 +206,13 @@ private class CodeFragmentCapturedValueVisitor( if (symbol.callableId == StandardClassIds.Callables.coroutineContext) { val isCrossingInlineBounds = isCrossingInlineBounds(element, symbol) val capturedValue = CodeFragmentCapturedValue.CoroutineContext(isCrossingInlineBounds) - register(symbol, CodeFragmentCapturedSymbol(capturedValue, symbol, symbol.resolvedReturnTypeRef)) + register(CodeFragmentCapturedSymbol(capturedValue, symbol, symbol.resolvedReturnTypeRef)) } } - private fun register(symbol: FirBasedSymbol<*>, mapping: CodeFragmentCapturedSymbol) { - val previousMapping = collectedMappings[symbol] + private fun register(mapping: CodeFragmentCapturedSymbol) { + val id = CodeFragmentCapturedId(mapping.symbol, mapping.contextReceiverNumber) + val previousMapping = collectedMappings[id] if (previousMapping != null) { val previousValue = previousMapping.value @@ -220,8 +226,8 @@ private class CodeFragmentCapturedValueVisitor( } } - collectedMappings[symbol] = mapping - registerFile(symbol) + collectedMappings[id] = mapping + registerFile(mapping.symbol) } private fun registerFile(symbol: FirBasedSymbol<*>) { 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 a5736a2559d..a781207247a 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 @@ -120,6 +120,24 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localMutated.kt"); } + @Test + @TestMetadata("multipleClassAndFunctionContextReceivers.kt") + public void testMultipleClassAndFunctionContextReceivers() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassAndFunctionContextReceivers.kt"); + } + + @Test + @TestMetadata("multipleClassContextReceivers.kt") + public void testMultipleClassContextReceivers() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleClassContextReceivers.kt"); + } + + @Test + @TestMetadata("multipleFunctionContextReceivers.kt") + public void testMultipleFunctionContextReceivers() throws Exception { + runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/multipleFunctionContextReceivers.kt"); + } + @Test @TestMetadata("nestedOuterClass.kt") public void testNestedOuterClass() throws Exception { diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/CodeFragmentUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/CodeFragmentUtils.kt index 442e339f699..693849eb645 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/CodeFragmentUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/CodeFragmentUtils.kt @@ -21,7 +21,7 @@ class CodeFragmentConversionData( val injectedValues: List ) -class InjectedValue(val symbol: FirBasedSymbol<*>, val typeRef: FirTypeRef, val isMutated: Boolean) { +class InjectedValue(val symbol: FirBasedSymbol<*>, val contextReceiverNumber: Int, val typeRef: FirTypeRef, val isMutated: Boolean) { val irParameterSymbol: IrValueParameterSymbol = IrValueParameterSymbolImpl() }