From e44e84d1b090475f063fa945e0df19db49ad3628 Mon Sep 17 00:00:00 2001 From: Nikita Bobko Date: Thu, 30 Nov 2023 19:04:55 +0100 Subject: [PATCH] [FIR] Reuse expect/actual matching data of outer declarations during matching of inner declarations Review: https://jetbrains.team/p/kt/reviews/13244 Motivation: ``` // Module :lib class Foo { val member: Int = 2 } // Module :app // dependencies { implementation(project(":lib")) } class Foo { val member: Int = 2 } ``` Before the commit: app_Foo.expectForActual is `null` app_Foo.member.expectForActual = lib_foo.member After the commit: app_Foo.expectForActual is `null` app_Foo.member.expectForActual is `null` If I don't fix this problem then `CommonizerHierarchicalIT.testCommonizeHierarchicallyMultiModule` becomes red after I fix KT-59887 in the following commits `app_Foo.member.expectForActual = lib_foo.member` happens because we also need to match fake-overrides (KT-63550) I didn't measure it, but theoretically, this commit should be a performance improvement, becuase we reuse `expectForActual` cache Additionally, The commit breaks some other tests (e.g. compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMissing.fir.kt). The tests will become green again, once I fix KT-59887 --- .../LLFirExpectActualMatcherLazyResolver.kt | 11 +++++++++++ .../transformers/mpp/FirExpectActualResolver.kt | 10 +++++----- .../actualClassWithDifferentConstructors.ll.kt | 2 +- .../multiplatform/headerClass/actualMissing.fir.kt | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirExpectActualMatcherLazyResolver.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirExpectActualMatcherLazyResolver.kt index e97ee8bf357..1cb8a471f3b 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirExpectActualMatcherLazyResolver.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirExpectActualMatcherLazyResolver.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector import org.jetbrains.kotlin.fir.resolve.transformers.mpp.FirExpectActualMatcherTransformer +import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase internal object LLFirExpectActualMatcherLazyResolver : LLFirLazyResolver(FirResolvePhase.EXPECT_ACTUAL_MATCHING) { override fun resolve( @@ -48,6 +49,16 @@ private class LLFirExpectActualMatchingTargetResolver( ) : LLFirTargetResolver(target, lockProvider, FirResolvePhase.EXPECT_ACTUAL_MATCHING) { private val enabled = session.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects) + @Deprecated("Should never be called directly, only for override purposes, please use withRegularClass", level = DeprecationLevel.ERROR) + override fun withRegularClassImpl(firClass: FirRegularClass, action: () -> Unit) { + if (enabled) { + // Resolve outer classes before resolving inner declarations. It's the requirement of FirExpectActualResolver + firClass.lazyResolveToPhase(resolverPhase.previous) + performResolve(firClass) + } + action() + } + private val transformer = object : FirExpectActualMatcherTransformer(session, scopeSession) { override fun transformRegularClass(regularClass: FirRegularClass, data: Nothing?): FirStatement { transformMemberDeclaration(regularClass) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt index b285c96df62..2dc8ef39e35 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt @@ -8,9 +8,9 @@ package org.jetbrains.kotlin.fir.resolve.transformers.mpp import org.jetbrains.kotlin.fir.FirExpectActualMatchingContext import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.ExpectForActualMatchingData +import org.jetbrains.kotlin.fir.declarations.expectForActual import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass import org.jetbrains.kotlin.fir.declarations.utils.isExpect -import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.providers.dependenciesSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope @@ -34,15 +34,15 @@ object FirExpectActualResolver { is FirCallableSymbol<*> -> { val callableId = actualSymbol.callableId val classId = callableId.classId - var expectContainingClass: FirRegularClassSymbol? = null var actualContainingClass: FirRegularClassSymbol? = null + var expectContainingClass: FirRegularClassSymbol? = null val candidates = when { classId != null -> { - expectContainingClass = useSiteSession.dependenciesSymbolProvider.getClassLikeSymbolByClassId(classId)?.let { - it.fullyExpandedClass(it.moduleData.session) - } actualContainingClass = useSiteSession.symbolProvider.getClassLikeSymbolByClassId(classId) ?.fullyExpandedClass(useSiteSession) + expectContainingClass = actualContainingClass?.fir?.expectForActual + ?.get(ExpectActualMatchingCompatibility.MatchedSuccessfully) + ?.singleOrNull() as? FirRegularClassSymbol when (actualSymbol) { is FirConstructorSymbol -> expectContainingClass?.getConstructors(expectScopeSession) diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDifferentConstructors.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDifferentConstructors.ll.kt index 649563e8234..42f73391273 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDifferentConstructors.ll.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDifferentConstructors.ll.kt @@ -46,7 +46,7 @@ actual class Bar5 { } class Bar6 { - actual constructor() + actual constructor() } actual class Bar7 actual constructor(s: String) { diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMissing.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMissing.fir.kt index 706414a06e9..a0d2ee32076 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMissing.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMissing.fir.kt @@ -9,5 +9,5 @@ expect class A { // FILE: jvm.kt class A { - actual fun foo() {} + actual fun foo() {} }