From abc1be2ee01d03313e5fbd6dfc043be0c3b6f3dd Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Thu, 29 Jun 2023 14:34:08 +0200 Subject: [PATCH] [LL FIR] KT-57743 Build dependency symbol providers lazily - Because `KtModule`s and thereby sessions may depend on each other cyclically, we must not create a session during the creation of another session. Otherwise, let's say session A and B depend on each other. Then we get the following nested creation: session A -> session B -> session A -> session B, and so on. - Dependency symbol providers are in part taken from dependency sessions. Hence, we have to create dependency symbol providers lazily. - For the creation of dependency symbol providers we only need to access the non-dependency symbol providers of a session dependency, so we don't need to access the dependency's dependency symbol providers. Hence, the lazy construction won't run into issues with cycles later on. ^KT-57743 fixed --- ...FirModuleWithDependenciesSymbolProvider.kt | 17 +++-- .../sessions/LLFirAbstractSessionFactory.kt | 63 ++++++++++--------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt index 1f9dbbd6e6b..57ed122c6e8 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt @@ -104,12 +104,19 @@ internal class LLFirModuleWithDependenciesSymbolProvider( internal class LLFirDependenciesSymbolProvider( session: FirSession, - val providers: List, + val computeProviders: () -> List, ) : FirSymbolProvider(session) { - init { - require(providers.all { it !is LLFirModuleWithDependenciesSymbolProvider }) { - "${LLFirDependenciesSymbolProvider::class.simpleName} may not contain ${LLFirModuleWithDependenciesSymbolProvider::class.simpleName}:" + - " dependency providers must be flattened during session creation." + /** + * Dependency symbol providers are lazy to support cyclic dependencies between modules. If a module A and a module B depend on each + * other and session creation tries to access dependency symbol providers eagerly, the creation of session A would try to create session + * B (to get its symbol providers), which in turn would try to create session A, and so on. + */ + val providers: List by lazy { + computeProviders().also { providers -> + require(providers.all { it !is LLFirModuleWithDependenciesSymbolProvider }) { + "${LLFirDependenciesSymbolProvider::class.simpleName} may not contain ${LLFirModuleWithDependenciesSymbolProvider::class.simpleName}:" + + " dependency providers must be flattened during session creation." + } } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt index 436ce311fb9..31c4d07e639 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt @@ -80,7 +80,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje builtinTypes, scope ), - LLFirDependenciesSymbolProvider(session, listOf(builtinSymbolProvider)), + LLFirDependenciesSymbolProvider(session) { listOf(builtinSymbolProvider) }, ) } @@ -101,8 +101,6 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje val components = LLFirModuleResolveComponents(module, globalResolveComponents, scopeProvider) - val dependencies = collectSourceModuleDependencies(module) - val session = LLFirScriptSession(module, components, builtinsSession.builtinTypes) components.session = session @@ -130,10 +128,12 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje register(FirProvider::class, provider) register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver()) - val dependencyProvider = LLFirDependenciesSymbolProvider(this, buildList { - addDependencySymbolProvidersTo(session, dependencies, this) - add(builtinsSession.symbolProvider) - }) + val dependencyProvider = LLFirDependenciesSymbolProvider(this) { + buildList { + addDependencySymbolProvidersTo(session, collectSourceModuleDependencies(module), this) + add(builtinsSession.symbolProvider) + } + } val javaSymbolProvider = LLFirJavaSymbolProvider(this, moduleData, project, provider.searchScope) register(JavaSymbolProvider::class, javaSymbolProvider) @@ -212,7 +212,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje register(FirProvider::class, provider) register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver()) - val dependencyProvider = LLFirDependenciesSymbolProvider(this, listOf(builtinsSession.symbolProvider)) + val dependencyProvider = LLFirDependenciesSymbolProvider(this) { listOf(builtinsSession.symbolProvider) } register( FirSymbolProvider::class, @@ -254,7 +254,6 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje val components = LLFirModuleResolveComponents(module, globalResolveComponents, scopeProvider) - val dependencies = collectSourceModuleDependencies(module) val session = LLFirSourcesSession(module, components, builtinsSession.builtinTypes) components.session = session @@ -284,10 +283,12 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje registerCompilerPluginExtensions(project, module) registerCommonComponentsAfterExtensionsAreConfigured() - val dependencyProvider = LLFirDependenciesSymbolProvider(this, buildList { - addDependencySymbolProvidersTo(session, dependencies, this) - add(builtinsSession.symbolProvider) - }) + val dependencyProvider = LLFirDependenciesSymbolProvider(this) { + buildList { + addDependencySymbolProvidersTo(session, collectSourceModuleDependencies(module), this) + add(builtinsSession.symbolProvider) + } + } register(DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY, dependencyProvider) register(LLFirFirClassByPsiClassProvider::class, LLFirFirClassByPsiClassProvider(this)) @@ -372,30 +373,32 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje register(FirRegisteredPluginAnnotations::class, LLFirIdeRegisteredPluginAnnotations(this, annotationsResolver)) register(FirPredicateBasedProvider::class, FirEmptyPredicateBasedProvider) - val dependencyProvider = LLFirDependenciesSymbolProvider(this, buildList { - add(builtinsSession.symbolProvider) + val dependencyProvider = LLFirDependenciesSymbolProvider(this) { + buildList { + add(builtinsSession.symbolProvider) - // Script dependencies are self-contained and should not depend on other libraries - if (module !is KtScriptDependencyModule) { - // Add all libraries excluding the current one - val librariesSearchScope = ProjectScope.getLibrariesScope(project) - .intersectWith(GlobalSearchScope.notScope(libraryModule.contentScope)) + // Script dependencies are self-contained and should not depend on other libraries + if (module !is KtScriptDependencyModule) { + // Add all libraries excluding the current one + val librariesSearchScope = ProjectScope.getLibrariesScope(project) + .intersectWith(GlobalSearchScope.notScope(libraryModule.contentScope)) - val restLibrariesProvider = createProjectLibraryProvidersForScope( - session, moduleData, scopeProvider, - project, builtinTypes, librariesSearchScope - ) + val restLibrariesProvider = createProjectLibraryProvidersForScope( + session, moduleData, scopeProvider, + project, builtinTypes, librariesSearchScope + ) - addAll(restLibrariesProvider) + addAll(restLibrariesProvider) - moduleAnchorSession?.let { - (it.symbolProvider as LLFirModuleWithDependenciesSymbolProvider).also { moduleSymbolProvider -> - addAll(moduleSymbolProvider.providers) - addAll(moduleSymbolProvider.dependencyProvider.providers) + moduleAnchorSession?.let { + (it.symbolProvider as LLFirModuleWithDependenciesSymbolProvider).also { moduleSymbolProvider -> + addAll(moduleSymbolProvider.providers) + addAll(moduleSymbolProvider.dependencyProvider.providers) + } } } } - }) + } register(DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY, dependencyProvider) register(LLFirFirClassByPsiClassProvider::class, LLFirFirClassByPsiClassProvider(this))