Extract control flow analysis to separate module

Extract a service interface out of ControlFlowInformationProviderImpl
and register its implementation in two "leaf" modules: 'cli',
'idea-core'.

This improves parallel build, since a lot of modules depend on
'frontend' but only these two modules reference the implementation and
thus depend on the full CFA implementation now.
This commit is contained in:
Alexander Udalov
2020-03-16 01:13:51 +01:00
committed by Alexander Udalov
parent c744515832
commit 2e2caae05c
70 changed files with 113 additions and 29 deletions

View File

@@ -230,6 +230,7 @@ extra["compilerModules"] = arrayOf(
":compiler:frontend",
":compiler:frontend.common",
":compiler:frontend.java",
":compiler:frontend:cfg",
":compiler:cli-common",
":compiler:ir.tree",
":compiler:ir.tree.impl",

View File

@@ -8,6 +8,7 @@ dependencies {
compile(project(":compiler:cli-common"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:frontend:cfg"))
compile(project(":compiler:backend-common"))
compile(project(":compiler:backend"))
compile(project(":compiler:backend.jvm"))

View File

@@ -5,10 +5,13 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.cfg.ControlFlowInformationProviderImpl
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useInstance
object CompilerEnvironment : TargetEnvironment("Compiler") {
override fun configure(container: StorageComponentContainer) {
configureCompilerEnvironment(container)
container.useInstance(ControlFlowInformationProviderImpl.Factory)
}
}

View File

@@ -0,0 +1,15 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":compiler:frontend"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("guava", rootProject = rootProject) }
}
sourceSets {
"main" { projectDefault() }
"test" {}
}

View File

@@ -63,14 +63,13 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.util.OperatorNameConventions
class ControlFlowInformationProvider private constructor(
class ControlFlowInformationProviderImpl private constructor(
private val subroutine: KtElement,
private val trace: BindingTrace,
private val pseudocode: Pseudocode,
private val languageVersionSettings: LanguageVersionSettings,
private val diagnosticSuppressor: PlatformDiagnosticSuppressor
) {
) : ControlFlowInformationProvider {
private val pseudocodeVariablesData by lazy {
PseudocodeVariablesData(pseudocode, trace.bindingContext)
}
@@ -88,14 +87,14 @@ class ControlFlowInformationProvider private constructor(
diagnosticSuppressor
)
fun checkForLocalClassOrObjectMode() {
override fun checkForLocalClassOrObjectMode() {
// Local classes and objects are analyzed twice: when TopDownAnalyzer processes it and as a part of its container.
// Almost all checks can be done when the container is analyzed
// except recording initialized variables (this information is needed for DeclarationChecker).
recordInitializedVariables()
}
fun checkDeclaration() {
override fun checkDeclaration() {
recordInitializedVariables()
@@ -123,7 +122,7 @@ class ControlFlowInformationProvider private constructor(
checkConstructorConsistency()
}
fun checkFunction(expectedReturnType: KotlinType?) {
override fun checkFunction(expectedReturnType: KotlinType?) {
val unreachableCode = collectUnreachableCode()
reportUnreachableCode(unreachableCode)
@@ -235,7 +234,7 @@ class ControlFlowInformationProvider private constructor(
val functionDescriptor = trace.bindingContext.get(DECLARATION_TO_DESCRIPTOR, element) as? CallableDescriptor
val expectedType = functionDescriptor?.returnType
val providerForLocalDeclaration = ControlFlowInformationProvider(
val providerForLocalDeclaration = ControlFlowInformationProviderImpl(
element, trace, localDeclarationInstruction.body, languageVersionSettings, diagnosticSuppressor
)
@@ -1122,7 +1121,7 @@ class ControlFlowInformationProvider private constructor(
KtTryExpression::class.java, KtFunction::class.java, KtAnonymousInitializer::class.java
) is KtTryExpression
private fun CallInstruction.isTailCall(subroutine: KtElement = this@ControlFlowInformationProvider.subroutine): Boolean {
private fun CallInstruction.isTailCall(subroutine: KtElement = this@ControlFlowInformationProviderImpl.subroutine): Boolean {
val tailInstructionDetector = TailInstructionDetector(subroutine)
return traverseFollowingInstructions(
this,
@@ -1221,6 +1220,16 @@ class ControlFlowInformationProvider private constructor(
map: MutableMap<Instruction, DiagnosticFactory<*>>
) : VariableContext(instruction, map)
object Factory : ControlFlowInformationProvider.Factory {
override fun createControlFlowInformationProvider(
declaration: KtElement,
trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings,
diagnosticSuppressor: PlatformDiagnosticSuppressor
): ControlFlowInformationProvider =
ControlFlowInformationProviderImpl(declaration, trace, languageVersionSettings, diagnosticSuppressor)
}
companion object {
private fun isUsedAsResultOfLambda(usages: List<Instruction>): Boolean {
for (usage in usages) {

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2010-2020 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.
*/
package org.jetbrains.kotlin.cfg
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.checkers.PlatformDiagnosticSuppressor
import org.jetbrains.kotlin.types.KotlinType
interface ControlFlowInformationProvider {
fun checkForLocalClassOrObjectMode()
fun checkDeclaration()
fun checkFunction(expectedReturnType: KotlinType?)
interface Factory {
fun createControlFlowInformationProvider(
declaration: KtElement,
trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings,
diagnosticSuppressor: PlatformDiagnosticSuppressor,
): ControlFlowInformationProvider
}
}

View File

@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.frontend.di
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.isTypeRefinementEnabled
import org.jetbrains.kotlin.container.StorageComponentContainer
@@ -136,6 +137,7 @@ fun createContainerForBodyResolve(
languageVersionSettings: LanguageVersionSettings,
moduleStructureOracle: ModuleStructureOracle,
sealedProvider: SealedClassInheritorsProvider,
controlFlowInformationProviderFactory: ControlFlowInformationProvider.Factory,
): StorageComponentContainer = createContainer("BodyResolve", analyzerServices) {
configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings, sealedProvider)
@@ -146,6 +148,7 @@ fun createContainerForBodyResolve(
useImpl<BodyResolver>()
useInstance(moduleStructureOracle)
useInstance(controlFlowInformationProviderFactory)
}
fun createContainerForLazyBodyResolve(
@@ -159,6 +162,7 @@ fun createContainerForLazyBodyResolve(
moduleStructureOracle: ModuleStructureOracle,
mainFunctionDetectorFactory: MainFunctionDetector.Factory,
sealedProvider: SealedClassInheritorsProvider,
controlFlowInformationProviderFactory: ControlFlowInformationProvider.Factory,
): StorageComponentContainer = createContainer("LazyBodyResolve", analyzerServices) {
configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings, sealedProvider)
useInstance(mainFunctionDetectorFactory)
@@ -169,6 +173,7 @@ fun createContainerForLazyBodyResolve(
useImpl<LazyTopDownAnalyzer>()
useImpl<BasicAbsentDescriptorHandler>()
useInstance(moduleStructureOracle)
useInstance(controlFlowInformationProviderFactory)
// All containers except common inject ExpectedActualDeclarationChecker, so for common we do that
// explicitly.
@@ -185,7 +190,8 @@ fun createContainerForLazyLocalClassifierAnalyzer(
languageVersionSettings: LanguageVersionSettings,
statementFilter: StatementFilter,
localClassDescriptorHolder: LocalClassDescriptorHolder,
analyzerServices: PlatformDependentAnalyzerServices
analyzerServices: PlatformDependentAnalyzerServices,
controlFlowInformationProviderFactory: ControlFlowInformationProvider.Factory,
): StorageComponentContainer = createContainer("LocalClassifierAnalyzer", analyzerServices) {
configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings)
@@ -198,6 +204,7 @@ fun createContainerForLazyLocalClassifierAnalyzer(
useInstance(NoTopLevelDescriptorProvider)
TargetEnvironment.configureCompilerEnvironment(this)
useInstance(controlFlowInformationProviderFactory)
useInstance(FileScopeProvider.ThrowException)
useImpl<AnnotationResolverImpl>()

View File

@@ -37,17 +37,20 @@ public class ControlFlowAnalyzer {
private final KotlinBuiltIns builtIns;
private final LanguageVersionSettings languageVersionSettings;
private final PlatformDiagnosticSuppressor diagnosticSuppressor;
private final ControlFlowInformationProvider.Factory controlFlowInformationProviderFactory;
public ControlFlowAnalyzer(
@NotNull BindingTrace trace,
@NotNull KotlinBuiltIns builtIns,
@NotNull LanguageVersionSettings languageVersionSettings,
@NotNull PlatformDiagnosticSuppressor diagnosticSuppressor
@NotNull PlatformDiagnosticSuppressor diagnosticSuppressor,
@NotNull ControlFlowInformationProvider.Factory controlFlowInformationProviderFactory
) {
this.trace = trace;
this.builtIns = builtIns;
this.languageVersionSettings = languageVersionSettings;
this.diagnosticSuppressor = diagnosticSuppressor;
this.controlFlowInformationProviderFactory = controlFlowInformationProviderFactory;
}
public void process(@NotNull BodiesResolveContext c) {
@@ -80,7 +83,9 @@ public class ControlFlowAnalyzer {
private void checkSecondaryConstructor(@NotNull KtSecondaryConstructor constructor) {
ControlFlowInformationProvider controlFlowInformationProvider =
new ControlFlowInformationProvider(constructor, trace, languageVersionSettings, diagnosticSuppressor);
controlFlowInformationProviderFactory.createControlFlowInformationProvider(
constructor, trace, languageVersionSettings, diagnosticSuppressor
);
controlFlowInformationProvider.checkDeclaration();
controlFlowInformationProvider.checkFunction(builtIns.getUnitType());
}
@@ -88,9 +93,10 @@ public class ControlFlowAnalyzer {
private void checkDeclarationContainer(@NotNull BodiesResolveContext c, KtDeclarationContainer declarationContainer) {
// A pseudocode of class/object initialization corresponds to a class/object
// or initialization of properties corresponds to a package declared in a file
ControlFlowInformationProvider controlFlowInformationProvider = new ControlFlowInformationProvider(
(KtElement) declarationContainer, trace, languageVersionSettings, diagnosticSuppressor
);
ControlFlowInformationProvider controlFlowInformationProvider =
controlFlowInformationProviderFactory.createControlFlowInformationProvider(
(KtElement) declarationContainer, trace, languageVersionSettings, diagnosticSuppressor
);
if (c.getTopDownAnalysisMode().isLocalDeclarations()) {
controlFlowInformationProvider.checkForLocalClassOrObjectMode();
return;
@@ -113,7 +119,9 @@ public class ControlFlowAnalyzer {
@NotNull BodiesResolveContext c, @NotNull KtDeclarationWithBody function, @Nullable KotlinType expectedReturnType
) {
ControlFlowInformationProvider controlFlowInformationProvider =
new ControlFlowInformationProvider(function, trace, languageVersionSettings, diagnosticSuppressor);
controlFlowInformationProviderFactory.createControlFlowInformationProvider(
function, trace, languageVersionSettings, diagnosticSuppressor
);
if (c.getTopDownAnalysisMode().isLocalDeclarations()) {
controlFlowInformationProvider.checkForLocalClassOrObjectMode();
return;

View File

@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.types.expressions
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.GlobalContext
@@ -71,7 +72,8 @@ class LocalClassifierAnalyzer(
private val kotlinTypeChecker: NewKotlinTypeChecker,
private val samConversionResolver: SamConversionResolver,
private val additionalClassPartsProvider: AdditionalClassPartsProvider,
private val sealedClassInheritorsProvider: SealedClassInheritorsProvider
private val sealedClassInheritorsProvider: SealedClassInheritorsProvider,
private val controlFlowInformationProviderFactory: ControlFlowInformationProvider.Factory,
) {
fun processClassOrObject(
scope: LexicalWritableScope?,
@@ -110,7 +112,8 @@ class LocalClassifierAnalyzer(
additionalClassPartsProvider,
sealedClassInheritorsProvider
),
analyzerServices
analyzerServices,
controlFlowInformationProviderFactory,
)
container.get<LazyTopDownAnalyzer>().analyzeDeclarations(
@@ -185,7 +188,8 @@ class LocalClassDescriptorHolder(
override val samConversionResolver: SamConversionResolver = this@LocalClassDescriptorHolder.samConversionResolver
override val additionalClassPartsProvider: AdditionalClassPartsProvider =
this@LocalClassDescriptorHolder.additionalClassPartsProvider
override val sealedClassInheritorsProvider: SealedClassInheritorsProvider = this@LocalClassDescriptorHolder.sealedClassInheritorsProvider
override val sealedClassInheritorsProvider: SealedClassInheritorsProvider =
this@LocalClassDescriptorHolder.sealedClassInheritorsProvider
},
containingDeclaration,
classOrObject.nameAsSafeName,

View File

@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.tests.di
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.cfg.ControlFlowInformationProviderImpl
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.getValue
@@ -43,6 +44,7 @@ fun createContainerForTests(project: Project, module: ModuleDescriptor): Contain
)
useImpl<AnnotationResolverImpl>()
useInstance(ModuleStructureOracle.SingleModule)
useInstance(ControlFlowInformationProviderImpl.Factory)
})
}

View File

@@ -16,6 +16,7 @@ import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cfg.ControlFlowInformationProviderImpl
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.GlobalContext
@@ -47,7 +48,6 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import org.jetbrains.kotlin.utils.checkWithAttachment
import java.util.*
import java.util.concurrent.locks.ReentrantLock
internal class PerFileAnalysisCache(val file: KtFile, componentProvider: ComponentProvider) {
@@ -484,8 +484,9 @@ private object KotlinResolveDataProvider {
analyzableElement.languageVersionSettings,
IdeaModuleStructureOracle(),
IdeMainFunctionDetectorFactory(),
IdeSealedClassInheritorsProvider
).get<LazyTopDownAnalyzer>()
IdeSealedClassInheritorsProvider,
ControlFlowInformationProviderImpl.Factory,
).get<LazyTopDownAnalyzer>()
lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement))
} finally {

View File

@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.idea.caches.resolve.util
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
import org.jetbrains.kotlin.cfg.ControlFlowInformationProviderImpl
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.BindingTrace
@@ -16,7 +16,7 @@ fun analyzeControlFlow(resolveSession: ResolveSession, resolveElement: KtElement
val controlFlowTrace = DelegatingBindingTrace(
trace.bindingContext, "Element control flow resolve", resolveElement, allowSliceRewrite = true
)
ControlFlowInformationProvider(
ControlFlowInformationProviderImpl(
resolveElement, controlFlowTrace, resolveElement.languageVersionSettings, resolveSession.platformDiagnosticSuppressor
).checkDeclaration()
controlFlowTrace.addOwnDataTo(trace, null, false)

View File

@@ -16,8 +16,10 @@
package org.jetbrains.kotlin.idea.project
import org.jetbrains.kotlin.cfg.ControlFlowInformationProviderImpl
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useImpl
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.idea.caches.lightClasses.LazyLightClassDataHolder
import org.jetbrains.kotlin.idea.compiler.IdeMainFunctionDetectorFactory
import org.jetbrains.kotlin.resolve.TargetEnvironment
@@ -30,5 +32,6 @@ object IdeaEnvironment : TargetEnvironment("Idea") {
container.useImpl<LazyLightClassDataHolder.DiagnosticsHolder>()
container.useImpl<IdeaModuleStructureOracle>()
container.useImpl<IdeMainFunctionDetectorFactory>()
container.useInstance(ControlFlowInformationProviderImpl.Factory)
}
}

View File

@@ -15,7 +15,7 @@ import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.SLRUCache
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
import org.jetbrains.kotlin.cfg.ControlFlowInformationProviderImpl
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.SimpleGlobalContext
import org.jetbrains.kotlin.context.withModule
@@ -44,7 +44,6 @@ import org.jetbrains.kotlin.resolve.lazy.*
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import java.util.*
import java.util.concurrent.ConcurrentMap
class ResolveElementCache(
@@ -647,7 +646,7 @@ class ResolveElementCache(
forceResolveAnnotationsInside(property)
for (accessor in property.accessors) {
ControlFlowInformationProvider(
ControlFlowInformationProviderImpl(
accessor, trace, accessor.languageVersionSettings, resolveSession.platformDiagnosticSuppressor
).checkDeclaration()
}
@@ -787,7 +786,8 @@ class ResolveElementCache(
targetPlatform.findAnalyzerServices(file.project),
file.languageVersionSettings,
IdeaModuleStructureOracle(),
IdeSealedClassInheritorsProvider
IdeSealedClassInheritorsProvider,
ControlFlowInformationProviderImpl.Factory,
).get()
}
@@ -845,4 +845,3 @@ class ResolveElementCache(
var forceFullAnalysisModeInTests: Boolean = false
}
}

View File

@@ -1,4 +1,3 @@
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -12,6 +11,7 @@ dependencies {
compile(project(":core:descriptors.jvm"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:frontend:cfg"))
compile(project(":compiler:light-classes"))
compile(project(":compiler:util"))
compile(project(":idea:ide-common"))

View File

@@ -62,6 +62,7 @@ val projectsToShadow by extra(listOf(
":compiler:frontend",
":compiler:frontend.common",
":compiler:frontend.java",
":compiler:frontend:cfg",
":idea",
":idea:idea-native",
":idea:idea-core",

View File

@@ -90,6 +90,7 @@ include ":benchmarks",
":compiler:frontend",
":compiler:frontend.common",
":compiler:frontend.java",
":compiler:frontend:cfg",
":kotlin-compiler-runner",
":compiler:cli-common",
":compiler:ir.tree",