[LL FIR] introduce lazyResolveRecursively API

FirFile after KT-56683 has its own phases and resolution logic,
so we should have a separate API for lazy resolution for FirFile and for
the entire file

^KT-61296 Fixed
This commit is contained in:
Dmitrii Gridin
2023-09-11 20:13:40 +02:00
committed by Space Team
parent b77ae5d275
commit 31b36ee766
16 changed files with 120 additions and 55 deletions
@@ -18,8 +18,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilt
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.LLFirWholeElementResolveTarget
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
@@ -51,6 +49,7 @@ import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.references.toResolvedSymbol
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.PsiIrFileEntry
@@ -291,7 +290,7 @@ internal class KtFirCompilerFacility(
private fun getFullyResolvedFirFile(file: KtFile): FirFile {
val firFile = file.getOrBuildFirFile(firResolveSession)
LLFirWholeElementResolveTarget(firFile).resolve(FirResolvePhase.BODY_RESOLVE)
firFile.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE)
return firFile
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.psi
@@ -30,9 +29,9 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedSymbolError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedTypeQualifierError
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.classId
@@ -59,7 +58,7 @@ internal class KtFirImportOptimizer(
val existingImports = file.importDirectives
if (existingImports.isEmpty()) return KtImportOptimizerResult()
val firFile = file.getOrBuildFirFile(firResolveSession).apply { lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) }
val firFile = file.getOrBuildFirFile(firResolveSession).apply { lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE) }
val explicitlyImportedFqNames = existingImports
.asSequence()
@@ -41,4 +41,16 @@ internal class LLFirLazyDeclarationResolver : FirLazyDeclarationResolver() {
toPhase = toPhase,
)
}
override fun lazyResolveToPhaseRecursively(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {
val fir = symbol.fir
val session = fir.moduleData.session
if (session !is LLFirResolvableModuleSession) return
val moduleComponents = session.moduleComponents
moduleComponents.firModuleLazyDeclarationResolver.lazyResolveRecursively(
target = fir,
scopeSession = moduleComponents.scopeSessionProvider.getScopeSession(),
toPhase = toPhase,
)
}
}
@@ -16,6 +16,8 @@ class LLFirSingleResolveTarget(
containerClasses: List<FirRegularClass>,
target: FirElementWithResolveState,
) : LLFirResolveTarget(firFile, containerClasses, target) {
constructor(firFile: FirFile) : this(firFile, emptyList(), firFile)
override fun visitTargetElement(
element: FirElementWithResolveState,
visitor: LLFirResolveTargetVisitor,
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
@@ -85,7 +86,7 @@ internal class FirElementBuilder(
private fun getOrBuildFirForKtFile(ktFile: KtFile): FirFile {
val firFile = moduleComponents.firFileBuilder.buildRawFirFileWithCaching(ktFile)
firFile.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
firFile.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE)
return firFile
}
@@ -31,7 +31,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
*
* Might resolve additional required declarations.
*
* Resolution is performed under the lock specific to each declaration which is going to be resolved.
* Resolution is performed under the lock specific to each declaration that is going to be resolved.
*
* Suitable for body resolve or/and on-air resolve.
*/
@@ -40,28 +40,8 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
) {
val fromPhase = target.resolvePhase
/**
* Currently [lazyResolve] on file means [LLFirWholeElementResolveTarget], but also [FirFile] itself
* has [resolvePhase] which does not match with the entire file resolution state.
* This additional [FirFile] condition can be dropped after KT-61296
*/
if (target !is FirFile && fromPhase >= toPhase) return
try {
resolveContainingFileToImports(target)
if (toPhase == FirResolvePhase.IMPORTS) return
lazyResolveTargets(
targets = LLFirResolveMultiDesignationCollector.getDesignationsToResolve(target),
scopeSession = scopeSession,
toPhase = toPhase,
towerDataContextCollector = null,
)
} catch (e: Exception) {
handleExceptionFromResolve(e, target, fromPhase, toPhase)
}
if (target.resolvePhase >= toPhase) return
lazyResolve(target, scopeSession, toPhase, LLFirResolveMultiDesignationCollector::getDesignationsToResolve)
}
/**
@@ -69,7 +49,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
*
* Might resolve additional required declarations.
*
* Resolution is performed under the lock specific to each declaration which is going to be resolved.
* Resolution is performed under the lock specific to each declaration that is going to be resolved.
*
* Suitable for body resolve or/and on-air resolve.
*/
@@ -77,6 +57,32 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
target: FirRegularClass,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
) {
lazyResolve(target, scopeSession, toPhase, LLFirResolveMultiDesignationCollector::getDesignationsToResolveWithCallableMembers)
}
/**
* Lazily resolves the [target] with nested declarations to a given [toPhase] recursively.
*
* Might resolve additional required declarations.
*
* Resolution is performed under the lock specific to each declaration that is going to be resolved.
*
* Suitable for body resolve or/and on-air resolve.
*/
fun lazyResolveRecursively(
target: FirElementWithResolveState,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
) {
lazyResolve(target, scopeSession, toPhase, LLFirResolveMultiDesignationCollector::getDesignationsToResolveRecursively)
}
private inline fun <T : FirElementWithResolveState> lazyResolve(
target: T,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
resolveTargets: (T) -> List<LLFirResolveTarget>,
) {
val fromPhase = target.resolvePhase
try {
@@ -84,7 +90,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
if (toPhase == FirResolvePhase.IMPORTS) return
lazyResolveTargets(
targets = LLFirResolveMultiDesignationCollector.getDesignationsToResolveWithCallableMembers(target),
targets = resolveTargets(target),
scopeSession = scopeSession,
toPhase = toPhase,
towerDataContextCollector = null,
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirClassWithAllCallablesResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirSingleResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeElementResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.asResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError
@@ -19,12 +20,10 @@ import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor
internal object LLFirResolveMultiDesignationCollector {
fun getDesignationsToResolve(target: FirElementWithResolveState): List<LLFirResolveTarget> {
return when (target) {
is FirFile -> listOf(LLFirWholeElementResolveTarget(target))
else -> getMainDesignationToResolve(target)?.withAnnotationContainer()
} ?: emptyList()
}
fun getDesignationsToResolve(target: FirElementWithResolveState): List<LLFirResolveTarget> = when (target) {
is FirFile -> listOf(LLFirSingleResolveTarget(target))
else -> getMainDesignationToResolve(target)?.withAnnotationContainer()
} ?: emptyList()
fun getDesignationsToResolveWithCallableMembers(target: FirRegularClass): List<LLFirResolveTarget> {
val designation = target.tryCollectDesignationWithFile() ?: return emptyList()
@@ -32,6 +31,14 @@ internal object LLFirResolveMultiDesignationCollector {
return resolveTarget.withAnnotationContainer()
}
fun getDesignationsToResolveRecursively(target: FirElementWithResolveState): List<LLFirResolveTarget> {
if (target is FirFile) return listOf(LLFirWholeElementResolveTarget(target))
val designation = target.tryCollectDesignationWithFile() ?: return emptyList()
val resolveTarget = LLFirWholeElementResolveTarget(designation.firFile, designation.path, target)
return resolveTarget.withAnnotationContainer()
}
private fun LLFirResolveTarget.withAnnotationContainer(): List<LLFirResolveTarget> {
val annotationsContainer = firFile.annotationsContainer
if (annotationsContainer?.shouldBeResolved() != true) return listOf(this)
@@ -1,5 +1,5 @@
RAW_FIR:
FILE: [ResolvedTo(IMPORTS)] fileElements.kt
FILE: [ResolvedTo(RAW_FIR)] fileElements.kt
@FILE:Deprecated[Unresolved](LAZY_EXPRESSION)
@FILE:Anno[Unresolved](LAZY_EXPRESSION)
[ResolvedTo(RAW_FIR)] annotations container
@@ -51,11 +51,7 @@ abstract class AbstractFirLazyDeclarationResolveTest : AbstractFirLazyDeclaratio
val session = firResolveSession.useSiteFirSession as LLFirResolvableModuleSession
val file = session.moduleComponents.firFileBuilder.buildRawFirFileWithCaching(ktFile)
file to fun(phase: FirResolvePhase) {
session.moduleComponents.firModuleLazyDeclarationResolver.lazyResolveTarget(
LLFirSingleResolveTarget(file, emptyList(), file),
phase,
towerDataContextCollector = null,
)
file.lazyResolveToPhase(phase)
}
}
else -> {
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirScriptSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
@@ -83,7 +83,7 @@ abstract class AbstractFirLazyDeclarationResolveTestCase : AbstractLowLevelApiSi
resolveWithClearCaches(ktFile) { llSession ->
checkSession(llSession)
val firFile = llSession.getOrBuildFirFile(ktFile)
firFile.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
firFile.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE)
if (resultBuilder.isNotEmpty()) {
resultBuilder.appendLine()
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability
@@ -64,7 +64,7 @@ class SealedClassesInheritorsCaclulatorPreAnalysisHandler(
firFiles: List<FirFile>,
tmpFirResolveSession: LLFirResolveSession
): Map<ClassId, List<ClassId>> {
firFiles.forEach { it.lazyResolveToPhase(FirResolvePhase.TYPES) }
firFiles.forEach { it.lazyResolveToPhaseRecursively(FirResolvePhase.TYPES) }
val inheritorsCollector = FirSealedClassInheritorsProcessor.InheritorsCollector(tmpFirResolveSession.useSiteFirSession)
val sealedClassInheritorsMap = mutableMapOf<FirRegularClass, MutableList<ClassId>>()
firFiles.forEach { it.accept(inheritorsCollector, sealedClassInheritorsMap) }
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerPro
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
@@ -70,7 +70,7 @@ internal fun testInBlockModification(
val declaration = elementToModify.getNonLocalContainingOrThisDeclaration() ?: file
val firDeclarationBefore = declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
val declarationToRender = if (dumpFirFile) {
file.getOrBuildFirFile(firSession).also { it.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) }
file.getOrBuildFirFile(firSession).also { it.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE) }
} else {
firDeclarationBefore
}
@@ -100,7 +100,7 @@ internal fun testInBlockModification(
val textAfter = if (dumpFirFile) {
// we should resolve the entire file instead of the declaration to be sure that this declaration will be
// resolved by file resolution as well
declarationToRender.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
declarationToRender.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE)
declarationToRender.render()
} else {
declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
@@ -16,4 +16,5 @@ object FirDummyCompilerLazyDeclarationResolver : FirLazyDeclarationResolver() {
override fun lazyResolveToPhase(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {}
override fun lazyResolveToPhaseWithCallableMembers(symbol: FirClassSymbol<*>, toPhase: FirResolvePhase) {}
override fun lazyResolveToPhaseRecursively(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {}
}
@@ -40,8 +40,20 @@ abstract class FirLazyDeclarationResolver : FirSessionComponent {
}
}
/**
* @see org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
*/
abstract fun lazyResolveToPhase(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase)
/**
* @see org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseWithCallableMembers
*/
abstract fun lazyResolveToPhaseWithCallableMembers(symbol: FirClassSymbol<*>, toPhase: FirResolvePhase)
/**
* @see org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
*/
abstract fun lazyResolveToPhaseRecursively(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase)
}
class FirLazyResolveContractViolationException(
@@ -104,7 +116,6 @@ fun FirDeclaration.lazyResolveToPhase(toPhase: FirResolvePhase) {
*
* @see lazyResolveToPhase
*/
fun FirClassSymbol<*>.lazyResolveToPhaseWithCallableMembers(toPhase: FirResolvePhase) {
fir.lazyDeclarationResolver.lazyResolveToPhaseWithCallableMembers(this, toPhase)
}
@@ -117,3 +128,31 @@ fun FirClassSymbol<*>.lazyResolveToPhaseWithCallableMembers(toPhase: FirResolveP
fun FirClass.lazyResolveToPhaseWithCallableMembers(toPhase: FirResolvePhase) {
symbol.lazyResolveToPhaseWithCallableMembers(toPhase)
}
/**
* Lazy resolve [FirBasedSymbol] and all nested declarations to [FirResolvePhase].
*
* In the case of lazy resolution (inside Analysis API), it checks that the declaration phase `>= toPhase`.
* If not, it resolves the declaration for the requested phase.
*
* If the [lazyResolveToPhase] is called inside a fir transformer,
* it should always request the phase which is strictly lower than the current transformer phase,
* otherwise a deadlock/StackOverflow is possible.
*
* For the compiler mode, it does nothing, as the compiler is non-lazy.
*
* @receiver [FirBasedSymbol] which should be resolved
* @param toPhase the minimum phase, the declaration and all nested declarations should be resolved to after an execution of the [lazyResolveToPhase]
*/
fun FirBasedSymbol<*>.lazyResolveToPhaseRecursively(toPhase: FirResolvePhase) {
fir.lazyDeclarationResolver.lazyResolveToPhaseRecursively(this, toPhase)
}
/**
* Lazy resolve [FirDeclaration] and all nested declarations to [FirResolvePhase].
*
* @see lazyResolveToPhaseRecursively
*/
fun FirDeclaration.lazyResolveToPhaseRecursively(toPhase: FirResolvePhase) {
symbol.lazyResolveToPhaseRecursively(toPhase)
}
@@ -1,5 +1,4 @@
// IGNORE_REVERSED_RESOLVE
// IGNORE_CONTRACT_VIOLATIONS
// FIR_IDENTICAL
@Retention(AnnotationRetention.SOURCE)
@Repeatable
@@ -28,6 +28,10 @@ class FirCompilerLazyDeclarationResolverWithPhaseChecking : FirLazyDeclarationRe
checkIfCanLazyResolveToPhase(toPhase)
}
override fun lazyResolveToPhaseRecursively(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {
checkIfCanLazyResolveToPhase(toPhase)
}
override fun startResolvingPhase(phase: FirResolvePhase) {
check(currentTransformerPhase == null)
currentTransformerPhase = phase