diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirSupertypeLazyResolver.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirSupertypeLazyResolver.kt index cddb8033d90..b89f86b1950 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirSupertypeLazyResolver.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirSupertypeLazyResolver.kt @@ -11,10 +11,13 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.asResolveTarg import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.session import org.jetbrains.kotlin.analysis.low.level.api.fir.api.tryCollectDesignationWithFile import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkTypeRefIsResolved import org.jetbrains.kotlin.fir.FirElementWithResolveState import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.utils.isActual import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass @@ -26,6 +29,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.SupertypeComputationSession import org.jetbrains.kotlin.fir.resolve.transformers.SupertypeComputationStatus import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector import org.jetbrains.kotlin.fir.resolve.transformers.platformSupertypeUpdater +import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.types.* @@ -157,8 +161,10 @@ private class LLFirSuperTypeTargetResolver( // 2. Resolve super declarations val status = supertypeComputationSession.getSupertypesComputationStatus(declaration) if (status is SupertypeComputationStatus.Computed) { - for (computedType in status.supertypeRefs) { - crawlSupertype(computedType.type) + supertypeComputationSession.withClassLikeDeclaration(declaration) { + for (computedType in status.supertypeRefs) { + crawlSupertype(computedType.type, resolveTargetSession) + } } } @@ -175,7 +181,7 @@ private class LLFirSuperTypeTargetResolver( } private fun FirClassLikeDeclaration.asResolveTarget(): LLFirSingleResolveTarget? { - return takeIf { canHaveLoopInSupertypesHierarchy(it, resolveTargetSession) } + return takeIf { supertypeComputationSession.canHaveLoopInSupertypesHierarchy(it) } ?.tryCollectDesignationWithFile() ?.asResolveTarget() } @@ -184,7 +190,7 @@ private class LLFirSuperTypeTargetResolver( LLFirSuperTypeTargetResolver( target = target, lockProvider = lockProvider, - scopeSession = scopeSession, + scopeSession = target.session.getScopeSession(), supertypeComputationSession = supertypeComputationSession, visitedElements = visitedElements, ).resolveDesignation() @@ -196,21 +202,41 @@ private class LLFirSuperTypeTargetResolver( * We want to apply resolved supertypes to as many designations as possible. * So we crawl the resolved supertypes of visited designations to find more designations to collect. */ - private fun crawlSupertype(type: ConeKotlinType) { - val classLikeDeclaration = type.toSymbol(resolveTargetSession)?.fir + private fun crawlSupertype(type: ConeKotlinType, declarationSiteSession: LLFirSession) { + // Resolve an 'expect' declaration before an 'actual' as it is like 'super' and 'sub' classes + for (session in listOf(declarationSiteSession, supertypeComputationSession.useSiteSession)) { + /** + * We can avoid deduplication here as the symbol will be checked with [visitedElements] + */ + type.toSymbol(session)?.let(::crawlSupertype) + } + + if (type is ConeClassLikeType) { + // The `classLikeDeclaration` is not associated with a file, and thus there is no need to resolve it, but it may still point + // to declarations via its type arguments which need to be collected and have a containing file. + // For example, a `Function1` could point to a type alias. + type.typeArguments.forEach { it.type?.let { crawlSupertype(it, declarationSiteSession) } } + } + } + + private fun crawlSupertype(symbol: FirClassifierSymbol<*>) { + val classLikeDeclaration = symbol.fir if (classLikeDeclaration !is FirClassLikeDeclaration) return if (classLikeDeclaration in visitedElements) return + if (classLikeDeclaration is FirJavaClass) { - if (!canHaveLoopInSupertypesHierarchy(classLikeDeclaration, resolveTargetSession)) return + if (!supertypeComputationSession.canHaveLoopInSupertypesHierarchy(classLikeDeclaration)) return visitedElements += classLikeDeclaration - val parentClass = classLikeDeclaration.outerClass(resolveTargetSession) + + val session = classLikeDeclaration.llFirSession + val parentClass = classLikeDeclaration.outerClass(session) if (parentClass != null) { - crawlSupertype(parentClass.defaultType()) + crawlSupertype(parentClass.defaultType(), session) } classLikeDeclaration.superTypeRefs.filterIsInstance().forEach { - crawlSupertype(it.type) + crawlSupertype(it.type, session) } return @@ -219,11 +245,6 @@ private class LLFirSuperTypeTargetResolver( val resolveTarget = classLikeDeclaration.asResolveTarget() if (resolveTarget != null) { resolveToSupertypePhase(resolveTarget) - } else if (type is ConeClassLikeType) { - // The `classLikeDeclaration` is not associated with a file, and thus there is no need to resolve it, but it may still point - // to declarations via its type arguments which need to be collected and have a containing file. - // For example, a `Function1` could point to a type alias. - type.typeArguments.forEach { it.type?.let(::crawlSupertype) } } } @@ -232,26 +253,6 @@ private class LLFirSuperTypeTargetResolver( } } -/** - * We shouldn't skip Java source classes because they're marked as BODY_RESOLVE, - * but this doesn't give us knowledge about its participation in the calculation of supertypes. - * The contract here – if a declaration is already resolved to FirResolvePhase.SUPER_TYPES or higher that this - * means that this class can't have loop with our class, because in this case this declaration will be present - * in the current supertypes resolve session - */ -private fun canHaveLoopInSupertypesHierarchy( - classLikeDeclaration: FirClassLikeDeclaration, - session: FirSession, -): Boolean = when { - classLikeDeclaration is FirJavaClass -> classLikeDeclaration.origin is FirDeclarationOrigin.Java.Source - classLikeDeclaration.origin !is FirDeclarationOrigin.Source -> false - classLikeDeclaration.resolvePhase < FirResolvePhase.SUPER_TYPES -> true - // We should still process resolved if it has loop in super type refs, because we can be part of this cycle - classLikeDeclaration is FirRegularClass && classLikeDeclaration.superTypeRefs.any(FirTypeRef::isLoopedSupertypeRef) -> true - classLikeDeclaration is FirTypeAlias && classLikeDeclaration.expandedTypeRef.isLoopedSupertypeRef -> true - else -> classLikeDeclaration.outerClass(session)?.let { canHaveLoopInSupertypesHierarchy(it, session) } == true -} - private fun FirClassLikeDeclaration.outerClass(session: FirSession): FirRegularClass? = symbol.classId.parentClassId?.let { parentClassId -> session.symbolProvider.getClassLikeSymbolByClassId(parentClassId)?.fir as? FirRegularClass } @@ -263,7 +264,40 @@ private val FirTypeRef.isLoopedSupertypeRef: Boolean return diagnostic is ConeSimpleDiagnostic && diagnostic.kind == DiagnosticKind.LoopInSupertype } -private class LLFirSupertypeComputationSession(private val useSiteSession: FirSession) : SupertypeComputationSession() { +private class LLFirSupertypeComputationSession(val useSiteSession: LLFirSession) : SupertypeComputationSession() { + private var shouldCheckForActualization: Boolean = false + + inline fun withClassLikeDeclaration(classLikeDeclaration: FirClassLikeDeclaration, transformer: (FirClassLikeDeclaration) -> Unit) { + val oldValue = shouldCheckForActualization + if (classLikeDeclaration.isActual) { + shouldCheckForActualization = true + } + + try { + transformer(classLikeDeclaration) + } finally { + shouldCheckForActualization = oldValue + } + } + + /** + * We shouldn't skip Java source classes because they're marked as BODY_RESOLVE, + * but this doesn't give us knowledge about its participation in the calculation of supertypes. + * The contract here – if a declaration is already resolved to FirResolvePhase.SUPER_TYPES or higher that this + * means that this class can't have loop with our class, because in this case this declaration will be present + * in the current supertypes resolve session + */ + fun canHaveLoopInSupertypesHierarchy(classLikeDeclaration: FirClassLikeDeclaration): Boolean = when { + classLikeDeclaration is FirJavaClass -> classLikeDeclaration.origin is FirDeclarationOrigin.Java.Source + classLikeDeclaration.origin !is FirDeclarationOrigin.Source -> false + shouldCheckForActualization -> true + classLikeDeclaration.resolvePhase < FirResolvePhase.SUPER_TYPES -> true + // We should still process resolved if it has loop in super type refs, because we can be part of this cycle + classLikeDeclaration is FirRegularClass && classLikeDeclaration.superTypeRefs.any(FirTypeRef::isLoopedSupertypeRef) -> true + classLikeDeclaration is FirTypeAlias && classLikeDeclaration.expandedTypeRef.isLoopedSupertypeRef -> true + else -> classLikeDeclaration.outerClass(classLikeDeclaration.llFirSession)?.let(::canHaveLoopInSupertypesHierarchy) == true + } + /** * These collections exist to reuse a collection for each search to avoid repeated memory allocation. * Can be replaced with a new collection on each invocation of [findLoopFor] @@ -302,7 +336,7 @@ private class LLFirSupertypeComputationSession(private val useSiteSession: FirSe } override fun isAlreadyResolved(classLikeDeclaration: FirClassLikeDeclaration): Boolean { - return !canHaveLoopInSupertypesHierarchy(classLikeDeclaration, useSiteSession) + return !canHaveLoopInSupertypesHierarchy(classLikeDeclaration) } /** diff --git a/analysis/low-level-api-fir/testData/lazyResolve/functions/actualOverride.txt b/analysis/low-level-api-fir/testData/lazyResolve/functions/actualOverride.txt index 25e894d6a9e..623482d9f96 100644 --- a/analysis/low-level-api-fir/testData/lazyResolve/functions/actualOverride.txt +++ b/analysis/low-level-api-fir/testData/lazyResolve/functions/actualOverride.txt @@ -1,7 +1,7 @@ RAW_FIR: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? open expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -45,8 +45,8 @@ FILE: [ResolvedTo(RAW_FIR)] jvm.kt IMPORTS: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? open expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -90,8 +90,8 @@ FILE: [ResolvedTo(IMPORTS)] jvm.kt COMPILER_REQUIRED_ANNOTATIONS: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? open expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -135,8 +135,8 @@ FILE: [ResolvedTo(IMPORTS)] jvm.kt COMPANION_GENERATION: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? open expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -180,8 +180,8 @@ FILE: [ResolvedTo(IMPORTS)] jvm.kt SUPER_TYPES: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? open expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -225,8 +225,8 @@ FILE: [ResolvedTo(IMPORTS)] jvm.kt TYPES: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? open expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| diff --git a/analysis/low-level-api-fir/testData/lazyResolve/functions/transitiveSubstitutionFakeOverride.txt b/analysis/low-level-api-fir/testData/lazyResolve/functions/transitiveSubstitutionFakeOverride.txt index c2744900ec0..9aa78de753f 100644 --- a/analysis/low-level-api-fir/testData/lazyResolve/functions/transitiveSubstitutionFakeOverride.txt +++ b/analysis/low-level-api-fir/testData/lazyResolve/functions/transitiveSubstitutionFakeOverride.txt @@ -1,7 +1,7 @@ RAW_FIR: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? final? expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -47,8 +47,8 @@ FILE: [ResolvedTo(RAW_FIR)] jvm.kt IMPORTS: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? final? expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -94,8 +94,8 @@ FILE: [ResolvedTo(IMPORTS)] jvm.kt COMPILER_REQUIRED_ANNOTATIONS: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? final? expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -141,8 +141,8 @@ FILE: [ResolvedTo(IMPORTS)] jvm.kt COMPANION_GENERATION: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? final? expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -188,8 +188,8 @@ FILE: [ResolvedTo(IMPORTS)] jvm.kt SUPER_TYPES: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? final? expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit| @@ -235,8 +235,8 @@ FILE: [ResolvedTo(IMPORTS)] jvm.kt TYPES: FILE: [ResolvedTo(IMPORTS)] common.kt - public? open expect [ResolvedTo(RAW_FIR)] class Base<[ResolvedTo(RAW_FIR)] T> : R|kotlin/Any| { - public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(RAW_FIR)] T>(): R|Base| + public? open expect [ResolvedTo(SUPER_TYPES)] class Base<[ResolvedTo(SUPER_TYPES)] T> : R|kotlin/Any| { + public? expect [ResolvedTo(RAW_FIR)] [ContainingClassKey=Base] constructor<[ResolvedTo(SUPER_TYPES)] T>(): R|Base| public? final? expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit|