diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/FirKotlinToJvmBytecodeCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/FirKotlinToJvmBytecodeCompiler.kt index 397f67b7f39..1c2a040a741 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/FirKotlinToJvmBytecodeCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/FirKotlinToJvmBytecodeCompiler.kt @@ -13,6 +13,7 @@ import com.intellij.openapi.vfs.VirtualFileSystem import com.intellij.psi.PsiElementFinder import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.ProjectScope +import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics import org.jetbrains.kotlin.asJava.finder.JavaElementFinder import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig @@ -30,16 +31,17 @@ import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots import org.jetbrains.kotlin.codegen.ClassBuilderFactories import org.jetbrains.kotlin.codegen.CodegenFactory import org.jetbrains.kotlin.codegen.state.GenerationState -import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.JVMConfigurationKeys -import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.fir.DependencyListForCliModule import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic import org.jetbrains.kotlin.fir.backend.Fir2IrResult import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendExtension import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers +import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider +import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.session.FirSessionFactory import org.jetbrains.kotlin.fir.session.FirSessionFactory.createSessionWithDependencies import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackagePartProvider @@ -48,10 +50,16 @@ import org.jetbrains.kotlin.modules.Module import org.jetbrains.kotlin.modules.TargetId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.CommonPlatforms +import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices +import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource +import org.jetbrains.kotlin.utils.addToStdlib.flattenTo +import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize import java.io.File import kotlin.collections.set @@ -148,11 +156,13 @@ object FirKotlinToJvmBytecodeCompiler { } private fun CompilationContext.runFrontend(ktFiles: List): FirAnalyzerFacade? { + @Suppress("NAME_SHADOWING") + var ktFiles = ktFiles val syntaxErrors = ktFiles.fold(false) { errorsFound, ktFile -> AnalyzerWithCompilerReport.reportSyntaxErrors(ktFile, environment.messageCollector).isHasErrors or errorsFound } - val sourceScope = GlobalSearchScope.filesWithoutLibrariesScope(project, ktFiles.map { it.virtualFile }) + var sourceScope = GlobalSearchScope.filesWithoutLibrariesScope(project, ktFiles.map { it.virtualFile }) .uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(project)) var librariesScope = ProjectScope.getLibrariesScope(project) @@ -164,35 +174,78 @@ object FirKotlinToJvmBytecodeCompiler { } val languageVersionSettings = moduleConfiguration.languageVersionSettings - val session = createSessionWithDependencies( - Name.identifier(module.getModuleName()), + + val commonKtFiles = ktFiles.filter { it.isCommonSource == true } + + val sessionProvider = FirProjectSessionProvider() + + fun createSession( + name: String, + platform: TargetPlatform, + analyzerServices: PlatformDependentAnalyzerServices, + sourceScope: GlobalSearchScope, + dependenciesConfigurator: DependencyListForCliModule.Builder.() -> Unit = {} + ): FirSession { + return createSessionWithDependencies( + Name.identifier(name), + platform, + analyzerServices, + externalSessionProvider = sessionProvider, + project, + languageVersionSettings, + sourceScope, + librariesScope, + lookupTracker = environment.configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER), + providerAndScopeForIncrementalCompilation, + getPackagePartProvider = { environment.createPackagePartProvider(it) }, + dependenciesConfigurator = { + dependencies(moduleConfiguration.jvmClasspathRoots.map { it.toPath() }) + dependencies(moduleConfiguration.jvmModularRoots.map { it.toPath() }) + friendDependencies(moduleConfiguration[JVMConfigurationKeys.FRIEND_PATHS] ?: emptyList()) + dependenciesConfigurator() + }, + sessionConfigurator = { + if (extendedAnalysisMode) { + registerExtendedCommonCheckers() + } + } + ) + } + + val commonSession = runIf( + languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects) && commonKtFiles.isNotEmpty() + ) { + val commonSourcesScope = GlobalSearchScope.filesWithoutLibrariesScope(project, commonKtFiles.map { it.virtualFile }) + sourceScope = sourceScope.intersectWith(GlobalSearchScope.notScope(commonSourcesScope)) + ktFiles = ktFiles.filterNot { it.isCommonSource == true } + createSession( + "${module.getModuleName()}-common", + CommonPlatforms.defaultCommonPlatform, + CommonPlatformAnalyzerServices, + commonSourcesScope + ) + } + + val session = createSession( + module.getModuleName(), JvmPlatforms.unspecifiedJvmPlatform, JvmPlatformAnalyzerServices, - externalSessionProvider = null, - project, - languageVersionSettings, - sourceScope, - librariesScope, - lookupTracker = environment.configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER), - providerAndScopeForIncrementalCompilation, - getPackagePartProvider = { environment.createPackagePartProvider(it) }, - dependenciesConfigurator = { - dependencies(moduleConfiguration.jvmClasspathRoots.map { it.toPath() }) - dependencies(moduleConfiguration.jvmModularRoots.map { it.toPath() }) - friendDependencies(moduleConfiguration[JVMConfigurationKeys.FRIEND_PATHS] ?: emptyList()) - }, - sessionConfigurator = { - if (extendedAnalysisMode) { - registerExtendedCommonCheckers() - } + sourceScope + ) { + if (commonSession != null) { + sourceDependsOnDependencies(listOf(commonSession.moduleData)) } - ) + } + val commonAnalyzerFacade = commonSession?.let { FirAnalyzerFacade(it, languageVersionSettings, commonKtFiles) } val firAnalyzerFacade = FirAnalyzerFacade(session, languageVersionSettings, ktFiles) + commonAnalyzerFacade?.runResolution() + val allFirDiagnostics = mutableListOf() + commonAnalyzerFacade?.runCheckers()?.values?.flattenTo(allFirDiagnostics) firAnalyzerFacade.runResolution() - val firDiagnostics = firAnalyzerFacade.runCheckers().values.flatten() - val hasErrors = FirDiagnosticsCompilerResultsReporter.reportDiagnostics(firDiagnostics, environment.messageCollector) + firAnalyzerFacade.runCheckers().values.flattenTo(allFirDiagnostics) + val hasErrors = FirDiagnosticsCompilerResultsReporter.reportDiagnostics(allFirDiagnostics, environment.messageCollector) return firAnalyzerFacade.takeUnless { syntaxErrors || hasErrors } } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/analysis/FirAnalyzerFacade.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/analysis/FirAnalyzerFacade.kt index 0467e4cf05b..fd672918e64 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/analysis/FirAnalyzerFacade.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/analysis/FirAnalyzerFacade.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.builder.PsiHandlingMode import org.jetbrains.kotlin.fir.builder.RawFirBuilder import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir +import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.firProvider import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl @@ -101,8 +102,13 @@ class FirAnalyzerFacade( if (_scopeSession == null) runResolution() val signaturer = JvmIdSignatureDescriptor(JvmDescriptorMangler(null)) + val commonFirFiles = session.moduleData.dependsOnDependencies + .map { it.session } + .filter { it.kind == FirSession.Kind.Source } + .flatMap { (it.firProvider as FirProviderImpl).getAllFirFiles() } + return Fir2IrConverter.createModuleFragment( - session, _scopeSession!!, firFiles!!, + session, _scopeSession!!, firFiles!! + commonFirFiles, languageVersionSettings, signaturer, extensions, FirJvmKotlinMangler(session), IrFactoryImpl, FirJvmVisibilityConverter, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirProviderImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirProviderImpl.kt index 90b92751d61..5adb84c5be4 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirProviderImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirProviderImpl.kt @@ -290,6 +290,10 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli override fun getClassNamesInPackage(fqName: FqName): Set { return state.classesInPackage[fqName] ?: emptySet() } + + fun getAllFirFiles(): List { + return state.fileMap.values.flatten() + } } private const val rebuildIndex = true diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.args b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.args new file mode 100644 index 00000000000..d9ddd94e7b4 --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.args @@ -0,0 +1,10 @@ +$TESTDATA_DIR$/firMultiplatformCompilationWithError/common.kt +$TESTDATA_DIR$/firMultiplatformCompilationWithError/jvm.kt +-Xcommon-sources +$TESTDATA_DIR$/firMultiplatformCompilationWithError/common.kt +-Xuse-fir +-cp +. +-d +$TEMP_DIR$ +-XXLanguage\:+MultiPlatformProjects diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.out b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.out new file mode 100644 index 00000000000..9794674e865 --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.out @@ -0,0 +1,20 @@ +warning: advanced option value is passed in an obsolete form. Please use the '=' character to specify the value: -Xcommon-sources=... +warning: ATTENTION! +This build uses unsafe internal compiler arguments: + +-XXLanguage:+MultiPlatformProjects + +This mode is not recommended for production use, +as no stability/compatibility guarantees are given on +compiler or generated code. Use it at your own risk! + +warning: ATTENTION! + This build uses in-dev FIR: + -Xuse-fir +compiler/testData/cli/jvm/firMultiplatformCompilationWithError/jvm.kt:1:1: error: actual class 'A' has no corresponding members for expected class members: + + /A.foo + +actual interface A +^ +COMPILATION_ERROR diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithError/common.kt b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError/common.kt new file mode 100644 index 00000000000..477f8fa8465 --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError/common.kt @@ -0,0 +1,3 @@ +expect interface A { + fun foo() +} diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithError/jvm.kt b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError/jvm.kt new file mode 100644 index 00000000000..f6d3e8746a1 --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError/jvm.kt @@ -0,0 +1 @@ +actual interface A diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.args b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.args new file mode 100644 index 00000000000..3c64a034140 --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.args @@ -0,0 +1,10 @@ +$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/common.kt +$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/jvm.kt +-Xcommon-sources +$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/common.kt +-Xuse-fir +-cp +. +-d +$TEMP_DIR$ +-XXLanguage\:+MultiPlatformProjects diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.out b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.out new file mode 100644 index 00000000000..8ad7871a715 --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.out @@ -0,0 +1,14 @@ +warning: advanced option value is passed in an obsolete form. Please use the '=' character to specify the value: -Xcommon-sources=... +warning: ATTENTION! +This build uses unsafe internal compiler arguments: + +-XXLanguage:+MultiPlatformProjects + +This mode is not recommended for production use, +as no stability/compatibility guarantees are given on +compiler or generated code. Use it at your own risk! + +warning: ATTENTION! + This build uses in-dev FIR: + -Xuse-fir +OK diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors/common.kt b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors/common.kt new file mode 100644 index 00000000000..dc175c5e4f5 --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors/common.kt @@ -0,0 +1,5 @@ +expect interface A { + fun foo(): String +} + +fun getA(): A = null!! diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors/jvm.kt b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors/jvm.kt new file mode 100644 index 00000000000..6425c97ba2b --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors/jvm.kt @@ -0,0 +1,9 @@ +actual interface A { + actual fun foo(): String + fun bar(): String +} + +fun test(): String { + val a = getA() + return a.foo() + a.bar() +} diff --git a/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonEnum.kt b/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonEnum.kt index 16c7e104c78..cc77796e38f 100644 --- a/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonEnum.kt +++ b/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonEnum.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // !LANGUAGE: +MultiPlatformProjects // ISSUE: KT-20306 diff --git a/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedClass.kt b/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedClass.kt index 3ef4098087c..af974a9bd1e 100644 --- a/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedClass.kt +++ b/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedClass.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // !LANGUAGE: +MultiPlatformProjects // ISSUE: KT-44474 diff --git a/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedInterface.kt b/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedInterface.kt index 32ca8a8f67f..0ac1bc72b3d 100644 --- a/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedInterface.kt +++ b/compiler/testData/codegen/box/multiplatform/exhaustiveness/commonSealedInterface.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // !LANGUAGE: +MultiPlatformProjects // ISSUE: KT-44474 diff --git a/compiler/testData/codegen/box/multiplatform/hmpp/simple.kt b/compiler/testData/codegen/box/multiplatform/hmpp/simple.kt index e5b5d10f287..396fd6a1dc2 100644 --- a/compiler/testData/codegen/box/multiplatform/hmpp/simple.kt +++ b/compiler/testData/codegen/box/multiplatform/hmpp/simple.kt @@ -5,7 +5,6 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // MODULE: common diff --git a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java index 1ff685600d3..ba5ef27b262 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java @@ -311,6 +311,16 @@ public class CliTestGenerated extends AbstractCliTest { runTest("compiler/testData/cli/jvm/firHello.args"); } + @TestMetadata("firMultiplatformCompilationWithError.args") + public void testFirMultiplatformCompilationWithError() throws Exception { + runTest("compiler/testData/cli/jvm/firMultiplatformCompilationWithError.args"); + } + + @TestMetadata("firMultiplatformCompilationWithoutErrors.args") + public void testFirMultiplatformCompilationWithoutErrors() throws Exception { + runTest("compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.args"); + } + @TestMetadata("firStdlibDependency.args") public void testFirStdlibDependency() throws Exception { runTest("compiler/testData/cli/jvm/firStdlibDependency.args");