diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CodegenFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CodegenFactory.kt index b1d1bf12fa0..5ee16b245a5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CodegenFactory.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CodegenFactory.kt @@ -31,6 +31,11 @@ import org.jetbrains.kotlin.resolve.BindingContext interface CodegenFactory { fun convertToIr(input: IrConversionInput): BackendInput + // Extracts a part of the BackendInput which corresponds only to the specified source files. + // This is needed to support cyclic module dependencies, which are allowed in JPS, where frontend and psi2ir is run on sources of all + // modules combined, and then backend is run on each individual module. + fun getModuleChunkBackendInput(wholeBackendInput: BackendInput, sourceFiles: Collection): BackendInput + fun generateModule(state: GenerationState, input: BackendInput) class IrConversionInput( @@ -68,6 +73,11 @@ object DefaultCodegenFactory : CodegenFactory { override fun convertToIr(input: CodegenFactory.IrConversionInput): CodegenFactory.BackendInput = DummyOldBackendInput + override fun getModuleChunkBackendInput( + wholeBackendInput: CodegenFactory.BackendInput, + sourceFiles: Collection, + ): CodegenFactory.BackendInput = DummyOldBackendInput + override fun generateModule(state: GenerationState, input: CodegenFactory.BackendInput) { val filesInPackages = MultiMap() val filesInMultifileClasses = MultiMap() diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt index 255cc5ede9a..34e9f456987 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt @@ -33,8 +33,8 @@ import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.toLogger import org.jetbrains.kotlin.cli.jvm.config.* import org.jetbrains.kotlin.codegen.ClassBuilderFactories +import org.jetbrains.kotlin.codegen.CodegenFactory import org.jetbrains.kotlin.codegen.DefaultCodegenFactory -import org.jetbrains.kotlin.codegen.KotlinCodegenFacade import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration @@ -113,6 +113,8 @@ object KotlinToJVMBytecodeCompiler { val localFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) + val (codegenFactory, wholeBackendInput) = convertToIr(environment, result) + for (module in chunk) { ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() @@ -120,7 +122,8 @@ object KotlinToJVMBytecodeCompiler { if (!checkKotlinPackageUsage(environment.configuration, ktFiles)) return false val moduleConfiguration = projectConfiguration.applyModuleProperties(module, buildFile) - outputs[module] = generate(environment, moduleConfiguration, result, ktFiles, module) + val backendInput = codegenFactory.getModuleChunkBackendInput(wholeBackendInput, ktFiles) + outputs[module] = generate(environment, moduleConfiguration, result, ktFiles, module, codegenFactory, backendInput) } return writeOutputs(environment.project, projectConfiguration, chunk, outputs, mainClassFqName) @@ -234,7 +237,28 @@ object KotlinToJVMBytecodeCompiler { result.throwIfError() - return generate(environment, environment.configuration, result, environment.getSourceFiles(), null) + val (codegenFactory, backendInput) = convertToIr(environment, result) + return generate(environment, environment.configuration, result, environment.getSourceFiles(), null, codegenFactory, backendInput) + } + + private fun convertToIr(environment: KotlinCoreEnvironment, result: AnalysisResult): Pair { + val configuration = environment.configuration + val codegenFactory = + if (configuration.getBoolean(JVMConfigurationKeys.IR)) JvmIrCodegenFactory( + configuration, configuration.get(CLIConfigurationKeys.PHASE_CONFIG) + ) else DefaultCodegenFactory + + val input = CodegenFactory.IrConversionInput( + environment.project, + environment.getSourceFiles(), + configuration, + result.moduleDescriptor, + result.bindingContext, + configuration.languageVersionSettings, + false, + ) + val backendInput = codegenFactory.convertToIr(input) + return Pair(codegenFactory, backendInput) } fun analyze(environment: KotlinCoreEnvironment): AnalysisResult? { @@ -304,9 +328,11 @@ object KotlinToJVMBytecodeCompiler { configuration: CompilerConfiguration, result: AnalysisResult, sourceFiles: List, - module: Module? + module: Module?, + codegenFactory: CodegenFactory, + backendInput: CodegenFactory.BackendInput, ): GenerationState { - val generationState = GenerationState.Builder( + val state = GenerationState.Builder( environment.project, ClassBuilderFactories.BINARIES, result.moduleDescriptor, @@ -314,11 +340,7 @@ object KotlinToJVMBytecodeCompiler { sourceFiles, configuration ) - .codegenFactory( - if (configuration.getBoolean(JVMConfigurationKeys.IR)) JvmIrCodegenFactory( - configuration, configuration.get(CLIConfigurationKeys.PHASE_CONFIG) - ) else DefaultCodegenFactory - ) + .codegenFactory(codegenFactory) .withModule(module) .onIndependentPartCompilationEnd(createOutputFilesFlushingCallbackIfPossible(configuration)) .build() @@ -328,7 +350,14 @@ object KotlinToJVMBytecodeCompiler { val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER) performanceManager?.notifyGenerationStarted() - KotlinCodegenFacade.compileCorrectFiles(generationState) + state.beforeCompile() + + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + + codegenFactory.generateModule(state, backendInput) + + CodegenFactory.doCheckCancelled(state) + state.factory.done() performanceManager?.notifyGenerationFinished() @@ -336,13 +365,13 @@ object KotlinToJVMBytecodeCompiler { AnalyzerWithCompilerReport.reportDiagnostics( FilteredJvmDiagnostics( - generationState.collectedExtraJvmDiagnostics, + state.collectedExtraJvmDiagnostics, result.bindingContext.diagnostics ), environment.messageCollector ) ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() - return generationState + return state } } diff --git a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt index 62b04404e41..a6e1d46b041 100644 --- a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt +++ b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt @@ -27,8 +27,10 @@ import org.jetbrains.kotlin.ir.builders.TranslationPluginContext import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl import org.jetbrains.kotlin.ir.linkage.IrProvider import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl @@ -168,6 +170,21 @@ open class JvmIrCodegenFactory( return result.toList() } + override fun getModuleChunkBackendInput( + wholeBackendInput: CodegenFactory.BackendInput, + sourceFiles: Collection, + ): CodegenFactory.BackendInput { + wholeBackendInput as JvmIrBackendInput + + val moduleChunk = sourceFiles.toSet() + val wholeModule = wholeBackendInput.irModuleFragment + return wholeBackendInput.copy( + IrModuleFragmentImpl(wholeModule.descriptor, wholeModule.irBuiltins, wholeModule.files.filter { file -> + file.getKtFile() in moduleChunk + }) + ) + } + override fun generateModule(state: GenerationState, input: CodegenFactory.BackendInput) { val (irModuleFragment, symbolTable, customPhaseConfig, irProviders, extensions, backendExtension, notifyCodegenStart) = input as JvmIrBackendInput diff --git a/compiler/testData/cli/jvm/modulesWithDependencyCycle.args b/compiler/testData/cli/jvm/modulesWithDependencyCycle.args new file mode 100644 index 00000000000..193c2b68836 --- /dev/null +++ b/compiler/testData/cli/jvm/modulesWithDependencyCycle.args @@ -0,0 +1 @@ +-Xbuild-file=$TESTDATA_DIR$/modulesWithDependencyCycle.xml diff --git a/compiler/testData/cli/jvm/modulesWithDependencyCycle.out b/compiler/testData/cli/jvm/modulesWithDependencyCycle.out new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/cli/jvm/modulesWithDependencyCycle.out @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/cli/jvm/modulesWithDependencyCycle.xml b/compiler/testData/cli/jvm/modulesWithDependencyCycle.xml new file mode 100644 index 00000000000..fa4f2b04bcf --- /dev/null +++ b/compiler/testData/cli/jvm/modulesWithDependencyCycle.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/compiler/testData/cli/jvm/modulesWithDependencyCycleA.kt b/compiler/testData/cli/jvm/modulesWithDependencyCycleA.kt new file mode 100644 index 00000000000..0d934645509 --- /dev/null +++ b/compiler/testData/cli/jvm/modulesWithDependencyCycleA.kt @@ -0,0 +1,8 @@ +package a + +import b.Y + +class A + +class X(val y: Y? = null) +class Z : Y() diff --git a/compiler/testData/cli/jvm/modulesWithDependencyCycleB.kt b/compiler/testData/cli/jvm/modulesWithDependencyCycleB.kt new file mode 100644 index 00000000000..ad5dc0729ed --- /dev/null +++ b/compiler/testData/cli/jvm/modulesWithDependencyCycleB.kt @@ -0,0 +1,8 @@ +package b + +import a.A +import a.X + +class B(val a: A? = null) + +open class Y(val x: X? = null) diff --git a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java index 29e2c05b699..454db767187 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java @@ -656,6 +656,11 @@ public class CliTestGenerated extends AbstractCliTest { runTest("compiler/testData/cli/jvm/mixingArgfilesAndUsualArgs.args"); } + @TestMetadata("modulesWithDependencyCycle.args") + public void testModulesWithDependencyCycle() throws Exception { + runTest("compiler/testData/cli/jvm/modulesWithDependencyCycle.args"); + } + @TestMetadata("multipleTextRangesInDiagnosticsOrder.args") public void testMultipleTextRangesInDiagnosticsOrder() throws Exception { runTest("compiler/testData/cli/jvm/multipleTextRangesInDiagnosticsOrder.args");