[LL FIR] LLFirSuperTypeTargetResolver: pass correct session during resolution

We should use the declaration-site session to have stable
resolution order. The same scheme is applicable during
regular lazy resolution calls.
Also, this means that we should process 'expect' classes
before 'actual' like it was for 'super' and 'sub' classes

^KT-63547
This commit is contained in:
Dmitrii Gridin
2023-12-21 01:13:43 +01:00
committed by Space Team
parent 960a59d49a
commit de44b7dc3b
3 changed files with 95 additions and 61 deletions
@@ -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<FirResolvedTypeRef>().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)
}
/**
@@ -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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
public? open expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit|
@@ -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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
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<T>|
public? final? expect [ResolvedTo(RAW_FIR)] fun existingMethodInBase([ResolvedTo(RAW_FIR)] param: T): R|kotlin/Unit|