diff --git a/compiler/arguments/resources/kotlin-compiler-arguments.json b/compiler/arguments/resources/kotlin-compiler-arguments.json index 4fe6c6537638c..c3e219972843b 100644 --- a/compiler/arguments/resources/kotlin-compiler-arguments.json +++ b/compiler/arguments/resources/kotlin-compiler-arguments.json @@ -1115,6 +1115,37 @@ "removedVersion": null } }, + { + "name": "header", + "shortName": null, + "deprecatedName": null, + "description": { + "current": "Enable header compilation mode.\nIn this mode, the compiler produces class files that only contain the 'skeleton' of the classes to be\ncompiled but the method bodies of all the implementations are empty. This is used to speed up parallel compilation\nbuild systems where header libraries can be used to replace downstream dependencies for which we only need to\nsee the type names and method signatures required to compile a given translation unit.", + "valueInVersions": [] + }, + "delimiter": null, + "valueType": { + "type": "org.jetbrains.kotlin.arguments.dsl.types.BooleanType", + "isNullable": { + "current": false, + "valueInVersions": [] + }, + "defaultValue": { + "current": false, + "valueInVersions": [] + } + }, + "valueDescription": { + "current": null, + "valueInVersions": [] + }, + "releaseVersionsMetadata": { + "introducedVersion": "2.2.0", + "stabilizedVersion": "2.2.0", + "deprecatedVersion": null, + "removedVersion": null + } + }, { "name": "progressive", "shortName": null, diff --git a/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/CommonCompilerArguments.kt b/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/CommonCompilerArguments.kt index ffd49207c1e4c..76c07f3e986a5 100644 --- a/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/CommonCompilerArguments.kt +++ b/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/CommonCompilerArguments.kt @@ -55,6 +55,24 @@ val actualCommonCompilerArguments by compilerArgumentsLevel(CompilerArgumentsLev ) } + compilerArgument { + name = "header" + compilerName = "headerMode" + description = """ + Enable header compilation mode. + In this mode, the compiler produces class files that only contain the 'skeleton' of the classes to be + compiled but the method bodies of all the implementations are empty. This is used to speed up parallel compilation + build systems where header libraries can be used to replace downstream dependencies for which we only need to + see the type names and method signatures required to compile a given translation unit. + """.trimIndent().asReleaseDependent() + valueType = BooleanType.defaultFalse + + lifecycle( + introducedVersion = KotlinReleaseVersion.v2_2_0, + stabilizedVersion = KotlinReleaseVersion.v2_2_0, + ) + } + compilerArgument { name = "progressive" deprecatedName = "Xprogressive" diff --git a/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api b/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api index 884487ceacbef..26383d4d8ef36 100644 --- a/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api +++ b/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api @@ -161,6 +161,7 @@ public final class org/jetbrains/kotlin/buildtools/api/SourcesChanges$Unknown : public abstract interface class org/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments : org/jetbrains/kotlin/buildtools/api/arguments/CommonToolArguments { public static final field API_VERSION Lorg/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments$CommonCompilerArgument; public static final field Companion Lorg/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments$Companion; + public static final field HEADER Lorg/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments$CommonCompilerArgument; public static final field KOTLIN_HOME Lorg/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments$CommonCompilerArgument; public static final field LANGUAGE_VERSION Lorg/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments$CommonCompilerArgument; public static final field OPT_IN Lorg/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments$CommonCompilerArgument; diff --git a/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments.kt b/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments.kt index 2eb9d4535641c..9ff49f69c4766 100644 --- a/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments.kt +++ b/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/CommonCompilerArguments.kt @@ -72,6 +72,17 @@ public interface CommonCompilerArguments : CommonToolArguments { public val KOTLIN_HOME: CommonCompilerArgument = CommonCompilerArgument("KOTLIN_HOME", KotlinKotlinVersion(1, 1, 50)) + /** + * Enable header compilation mode. + * In this mode, the compiler produces class files that only contain the 'skeleton' of the classes to be + * compiled but the method bodies of all the implementations are empty. This is used to speed up parallel compilation + * build systems where header libraries can be used to replace downstream dependencies for which we only need to + * see the type names and method signatures required to compile a given translation unit. + */ + @JvmField + public val HEADER: CommonCompilerArgument = + CommonCompilerArgument("HEADER", KotlinKotlinVersion(2, 2, 0)) + /** * Enable progressive compiler mode. * In this mode, deprecations and bug fixes for unstable code take effect immediately diff --git a/compiler/build-tools/kotlin-build-tools-compat/gen/org/jetbrains/kotlin/buildtools/internal/compat/arguments/CommonCompilerArgumentsImpl.kt b/compiler/build-tools/kotlin-build-tools-compat/gen/org/jetbrains/kotlin/buildtools/internal/compat/arguments/CommonCompilerArgumentsImpl.kt index b00336bbbd8a2..c899df62caf8c 100644 --- a/compiler/build-tools/kotlin-build-tools-compat/gen/org/jetbrains/kotlin/buildtools/internal/compat/arguments/CommonCompilerArgumentsImpl.kt +++ b/compiler/build-tools/kotlin-build-tools-compat/gen/org/jetbrains/kotlin/buildtools/internal/compat/arguments/CommonCompilerArgumentsImpl.kt @@ -17,6 +17,7 @@ import kotlin.collections.MutableSet import kotlin.collections.mutableMapOf import kotlin.collections.mutableSetOf import org.jetbrains.kotlin.buildtools.`internal`.compat.arguments.CommonCompilerArgumentsImpl.Companion.API_VERSION +import org.jetbrains.kotlin.buildtools.`internal`.compat.arguments.CommonCompilerArgumentsImpl.Companion.HEADER import org.jetbrains.kotlin.buildtools.`internal`.compat.arguments.CommonCompilerArgumentsImpl.Companion.KOTLIN_HOME import org.jetbrains.kotlin.buildtools.`internal`.compat.arguments.CommonCompilerArgumentsImpl.Companion.LANGUAGE_VERSION import org.jetbrains.kotlin.buildtools.`internal`.compat.arguments.CommonCompilerArgumentsImpl.Companion.OPT_IN @@ -147,6 +148,7 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(), if (LANGUAGE_VERSION in this) { arguments.languageVersion = get(LANGUAGE_VERSION)?.stringValue} if (API_VERSION in this) { arguments.apiVersion = get(API_VERSION)?.stringValue} if (KOTLIN_HOME in this) { arguments.kotlinHome = get(KOTLIN_HOME)} + try { if (HEADER in this) { arguments.headerMode = get(HEADER)} } catch (e: NoSuchMethodError) { throw IllegalStateException("""Compiler parameter not recognized: HEADER. Current compiler version is: $KC_VERSION}, but the argument was introduced in 2.2.0""").initCause(e) } if (PROGRESSIVE in this) { arguments.progressiveMode = get(PROGRESSIVE)} if (SCRIPT in this) { arguments.script = get(SCRIPT)} try { if (X_REPL in this) { arguments.repl = get(X_REPL)} } catch (e: NoSuchMethodError) { throw IllegalStateException("""Compiler parameter not recognized: X_REPL. Current compiler version is: $KC_VERSION}, but the argument was introduced in 2.2.0""").initCause(e) } @@ -239,6 +241,7 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(), try { this[LANGUAGE_VERSION] = arguments.languageVersion?.let { EnumsKotlinVersion.entries.first { entry -> entry.stringValue == it } } } catch (_: NoSuchMethodError) { } try { this[API_VERSION] = arguments.apiVersion?.let { EnumsKotlinVersion.entries.first { entry -> entry.stringValue == it } } } catch (_: NoSuchMethodError) { } try { this[KOTLIN_HOME] = arguments.kotlinHome } catch (_: NoSuchMethodError) { } + try { this[HEADER] = arguments.headerMode } catch (_: NoSuchMethodError) { } try { this[PROGRESSIVE] = arguments.progressiveMode } catch (_: NoSuchMethodError) { } try { this[SCRIPT] = arguments.script } catch (_: NoSuchMethodError) { } try { this[X_REPL] = arguments.repl } catch (_: NoSuchMethodError) { } @@ -343,6 +346,8 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(), public val KOTLIN_HOME: CommonCompilerArgument = CommonCompilerArgument("KOTLIN_HOME") + public val HEADER: CommonCompilerArgument = CommonCompilerArgument("HEADER") + public val PROGRESSIVE: CommonCompilerArgument = CommonCompilerArgument("PROGRESSIVE") public val SCRIPT: CommonCompilerArgument = CommonCompilerArgument("SCRIPT") diff --git a/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/CommonCompilerArgumentsImpl.kt b/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/CommonCompilerArgumentsImpl.kt index e6704b8d1b47c..c4625a0410593 100644 --- a/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/CommonCompilerArgumentsImpl.kt +++ b/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/CommonCompilerArgumentsImpl.kt @@ -18,6 +18,7 @@ import kotlin.collections.mutableMapOf import kotlin.collections.mutableSetOf import org.jetbrains.kotlin.buildtools.`internal`.UseFromImplModuleRestricted import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.API_VERSION +import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.HEADER import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.KOTLIN_HOME import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.LANGUAGE_VERSION import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.OPT_IN @@ -155,6 +156,7 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(), if (LANGUAGE_VERSION in this) { arguments.languageVersion = get(LANGUAGE_VERSION)?.stringValue} if (API_VERSION in this) { arguments.apiVersion = get(API_VERSION)?.stringValue} if (KOTLIN_HOME in this) { arguments.kotlinHome = get(KOTLIN_HOME)} + if (HEADER in this) { arguments.headerMode = get(HEADER)} if (PROGRESSIVE in this) { arguments.progressiveMode = get(PROGRESSIVE)} if (SCRIPT in this) { arguments.script = get(SCRIPT)} if (X_REPL in this) { arguments.repl = get(X_REPL)} @@ -252,6 +254,7 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(), try { this[LANGUAGE_VERSION] = arguments.languageVersion?.let { EnumsKotlinVersion.entries.first { entry -> entry.stringValue == it } } } catch (_: NoSuchMethodError) { } try { this[API_VERSION] = arguments.apiVersion?.let { EnumsKotlinVersion.entries.first { entry -> entry.stringValue == it } } } catch (_: NoSuchMethodError) { } try { this[KOTLIN_HOME] = arguments.kotlinHome } catch (_: NoSuchMethodError) { } + try { this[HEADER] = arguments.headerMode } catch (_: NoSuchMethodError) { } try { this[PROGRESSIVE] = arguments.progressiveMode } catch (_: NoSuchMethodError) { } try { this[SCRIPT] = arguments.script } catch (_: NoSuchMethodError) { } try { this[X_REPL] = arguments.repl } catch (_: NoSuchMethodError) { } @@ -361,6 +364,8 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(), public val KOTLIN_HOME: CommonCompilerArgument = CommonCompilerArgument("KOTLIN_HOME") + public val HEADER: CommonCompilerArgument = CommonCompilerArgument("HEADER") + public val PROGRESSIVE: CommonCompilerArgument = CommonCompilerArgument("PROGRESSIVE") public val SCRIPT: CommonCompilerArgument = CommonCompilerArgument("SCRIPT") diff --git a/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt b/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt index 4adc9f7aa919d..5e6f8cdfc8287 100644 --- a/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt +++ b/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt @@ -59,6 +59,20 @@ abstract class CommonCompilerArguments : CommonToolArguments() { field = if (value.isNullOrEmpty()) null else value } + @Argument( + value = "-header", + description = """Enable header compilation mode. +In this mode, the compiler produces class files that only contain the 'skeleton' of the classes to be +compiled but the method bodies of all the implementations are empty. This is used to speed up parallel compilation +build systems where header libraries can be used to replace downstream dependencies for which we only need to +see the type names and method signatures required to compile a given translation unit.""", + ) + var headerMode: Boolean = false + set(value) { + checkFrozen() + field = value + } + @Argument( value = "-progressive", deprecatedName = "-Xprogressive", diff --git a/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArgumentsCopyGenerated.kt b/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArgumentsCopyGenerated.kt index 62815e2be7994..11132b86c904e 100644 --- a/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArgumentsCopyGenerated.kt +++ b/compiler/cli/cli-common/gen/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArgumentsCopyGenerated.kt @@ -47,6 +47,7 @@ fun copyCommonCompilerArguments(from: CommonCompilerArguments, to: CommonCompile to.fragmentRefines = from.fragmentRefines?.copyOf() to.fragmentSources = from.fragmentSources?.copyOf() to.fragments = from.fragments?.copyOf() + to.headerMode = from.headerMode to.ignoreConstOptimizationErrors = from.ignoreConstOptimizationErrors to.incrementalCompilation = from.incrementalCompilation to.inlineClasses = from.inlineClasses diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/WebFrontendPipelinePhase.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/WebFrontendPipelinePhase.kt index e453cc29645a3..0a2880a624587 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/WebFrontendPipelinePhase.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/WebFrontendPipelinePhase.kt @@ -194,7 +194,13 @@ object WebFrontendPipelinePhase : PipelinePhase groupedSources.fileBelongsToModuleForLt(file, it) }, buildResolveAndCheckFir = { session, files -> - buildResolveAndCheckFirViaLightTree(session, files, diagnosticsReporter, performanceManager?.let { it::addSourcesStats }) + buildResolveAndCheckFirViaLightTree( + session, + files, + diagnosticsReporter, + performanceManager?.let { it::addSourcesStats }, + false + ) }, useWasmPlatform = useWasmPlatform, ) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/arguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/arguments.kt index 41fa2a4368ed7..c3957e949adc4 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/arguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/arguments.kt @@ -30,6 +30,7 @@ fun CompilerConfiguration.setupCommonArguments( val messageCollector = getNotNull(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY) put(CommonConfigurationKeys.DISABLE_INLINE, arguments.noInline) + put(CommonConfigurationKeys.HEADER_COMPILATION, arguments.headerMode) put(CommonConfigurationKeys.USE_FIR_EXTRA_CHECKERS, arguments.extraWarnings) put(CommonConfigurationKeys.METADATA_KLIB, arguments.metadataKlib) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/klibArguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/klibArguments.kt index ccce8651e5dc5..de802c466ca57 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/klibArguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/klibArguments.kt @@ -76,6 +76,9 @@ fun CompilerConfiguration.copyCommonKlibArgumentsFrom(source: CompilerConfigurat klibAbiCompatibilityLevel = source.klibAbiCompatibilityLevel zipFileSystemAccessor = source.zipFileSystemAccessor + + // Header compilation mode needs to be propagated to KLIB compilation. + headerCompilation = source.headerCompilation } private fun parseCustomKotlinAbiVersion(customKlibAbiVersion: String?, collector: MessageCollector): KotlinAbiVersion? { diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/legacy/pipeline/jvmIncrementalCompilerPipelineLightTree.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/legacy/pipeline/jvmIncrementalCompilerPipelineLightTree.kt index 98e43721f4f5e..0ffdd4cd7514f 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/legacy/pipeline/jvmIncrementalCompilerPipelineLightTree.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/legacy/pipeline/jvmIncrementalCompilerPipelineLightTree.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.prepareJvmSessions import org.jetbrains.kotlin.cli.jvm.compiler.VfsBasedProjectEnvironment import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.headerCompilation import org.jetbrains.kotlin.config.perfManager import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar @@ -94,9 +95,9 @@ private fun FrontendContext.compileModuleToAnalyzedFirViaLightTreeIncrementally( ) val countFilesAndLines = if (performanceManager == null) null else performanceManager::addSourcesStats - val outputs = sessionsWithSources.map { (session, sources) -> - buildResolveAndCheckFirViaLightTree(session, sources, diagnosticsReporter, countFilesAndLines) + buildResolveAndCheckFirViaLightTree(session, sources, diagnosticsReporter, countFilesAndLines, + configuration.headerCompilation) } outputs.runPlatformCheckers(diagnosticsReporter) FirResult(outputs) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/pipeline/jvm/JvmFrontendPipelinePhase.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/pipeline/jvm/JvmFrontendPipelinePhase.kt index f91ab586adaac..7faebf81f121b 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/pipeline/jvm/JvmFrontendPipelinePhase.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/pipeline/jvm/JvmFrontendPipelinePhase.kt @@ -263,7 +263,8 @@ object JvmFrontendPipelinePhase : PipelinePhase val rawFirFiles = when (configuration.useLightTree) { - true -> session.buildFirViaLightTree(sources, diagnosticsCollector, countFilesAndLines) + true -> session.buildFirViaLightTree(sources, diagnosticsCollector, countFilesAndLines, + configuration.headerCompilation) else -> session.buildFirFromKtFiles(sources.asKtFilesList()) } resolveAndCheckFir(session, rawFirFiles, diagnosticsCollector) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/pipeline/metadata/MetadataFrontendPipelinePhase.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/pipeline/metadata/MetadataFrontendPipelinePhase.kt index ce1a1b8887c37..ed6244ef7e7d0 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/pipeline/metadata/MetadataFrontendPipelinePhase.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/pipeline/metadata/MetadataFrontendPipelinePhase.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.cli.pipeline.PerformanceNotifications import org.jetbrains.kotlin.cli.pipeline.PipelinePhase import org.jetbrains.kotlin.cli.pipeline.jvm.asKtFilesList import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.headerCompilation import org.jetbrains.kotlin.config.messageCollector import org.jetbrains.kotlin.config.moduleName import org.jetbrains.kotlin.config.perfManager @@ -110,9 +111,10 @@ object MetadataFrontendPipelinePhase : PipelinePhase - val firFiles = session.buildFirViaLightTree(files, diagnosticsReporter) { files, lines -> - perfManager?.addSourcesStats(files, lines) - } + val firFiles = session.buildFirViaLightTree(files, diagnosticsReporter, { files, lines -> + perfManager?.addSourcesStats(files, lines) + }, + configuration.headerCompilation) resolveAndCheckFir(session, firFiles, diagnosticsReporter) } } else { diff --git a/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/CommonConfigurationKeysContainer.kt b/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/CommonConfigurationKeysContainer.kt index 44216a860cc26..749df5f2e867a 100644 --- a/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/CommonConfigurationKeysContainer.kt +++ b/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/CommonConfigurationKeysContainer.kt @@ -44,6 +44,7 @@ object CommonConfigurationKeysContainer : KeysContainer("org.jetbrains.kotlin.co val PARALLEL_BACKEND_THREADS by key("Run codegen phase in parallel with N threads") val DUMP_MODEL by key("Dump compilation model") val INCREMENTAL_COMPILATION by key("Enable incremental compilation") + val HEADER_COMPILATION by key("Enable header compilation mode") val ALLOW_ANY_SCRIPTS_IN_SOURCE_ROOTS by key("Allow to compile any scripts along with regular Kotlin sources") val IGNORE_CONST_OPTIMIZATION_ERRORS by key("Ignore errors from IrConstTransformer") val EVALUATED_CONST_TRACKER by key("Keeps track of all evaluated by IrInterpreter constants") diff --git a/compiler/config/gen/org/jetbrains/kotlin/config/CommonConfigurationKeys.kt b/compiler/config/gen/org/jetbrains/kotlin/config/CommonConfigurationKeys.kt index 5113aabb661b3..16612216ba4b4 100644 --- a/compiler/config/gen/org/jetbrains/kotlin/config/CommonConfigurationKeys.kt +++ b/compiler/config/gen/org/jetbrains/kotlin/config/CommonConfigurationKeys.kt @@ -88,6 +88,9 @@ object CommonConfigurationKeys { @JvmField val INCREMENTAL_COMPILATION = CompilerConfigurationKey.create("Enable incremental compilation") + @JvmField + val HEADER_COMPILATION = CompilerConfigurationKey.create("Enable header compilation mode") + @JvmField val ALLOW_ANY_SCRIPTS_IN_SOURCE_ROOTS = CompilerConfigurationKey.create("Allow to compile any scripts along with regular Kotlin sources") @@ -218,6 +221,10 @@ var CompilerConfiguration.incrementalCompilation: Boolean get() = getBoolean(CommonConfigurationKeys.INCREMENTAL_COMPILATION) set(value) { put(CommonConfigurationKeys.INCREMENTAL_COMPILATION, value) } +var CompilerConfiguration.headerCompilation: Boolean + get() = getBoolean(CommonConfigurationKeys.HEADER_COMPILATION) + set(value) { put(CommonConfigurationKeys.HEADER_COMPILATION, value) } + var CompilerConfiguration.allowAnyScriptsInSourceRoots: Boolean get() = getBoolean(CommonConfigurationKeys.ALLOW_ANY_SCRIPTS_IN_SOURCE_ROOTS) set(value) { put(CommonConfigurationKeys.ALLOW_ANY_SCRIPTS_IN_SOURCE_ROOTS, value) } diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/testFixtures/org/jetbrains/kotlin/fir/java/AbstractFirOldFrontendLightClassesTest.kt b/compiler/fir/analysis-tests/legacy-fir-tests/testFixtures/org/jetbrains/kotlin/fir/java/AbstractFirOldFrontendLightClassesTest.kt index 312bf3a5864ed..1b1f2bfa299a7 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/testFixtures/org/jetbrains/kotlin/fir/java/AbstractFirOldFrontendLightClassesTest.kt +++ b/compiler/fir/analysis-tests/legacy-fir-tests/testFixtures/org/jetbrains/kotlin/fir/java/AbstractFirOldFrontendLightClassesTest.kt @@ -121,7 +121,7 @@ abstract class AbstractFirOldFrontendLightClassesTest : BaseDiagnosticsTest() { private fun mapKtFilesToFirFiles(session: FirSession, ktFiles: List, firFiles: MutableList, useLightTree: Boolean) { val firProvider = (session.firProvider as FirProviderImpl) if (useLightTree) { - val lightTreeBuilder = LightTree2Fir(session, firProvider.kotlinScopeProvider) + val lightTreeBuilder = LightTree2Fir(session, false, firProvider.kotlinScopeProvider) ktFiles.mapTo(firFiles) { val firFile = lightTreeBuilder.buildFirFile( diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/firUtils.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/firUtils.kt index 14e057025b84b..27f51bef8a70c 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/firUtils.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/firUtils.kt @@ -24,10 +24,11 @@ fun FirSession.buildFirViaLightTree( files: Collection, diagnosticsReporter: DiagnosticReporter?, reportFilesAndLines: ((Int, Int) -> Unit)?, + useHeaderCompilation: Boolean, ): List { val firProvider = (firProvider as FirProviderImpl) val sourcesToPathsMapper = sourcesToPathsMapper - val builder = LightTree2Fir(this, firProvider.kotlinScopeProvider, diagnosticsReporter) + val builder = LightTree2Fir(this, useHeaderCompilation,firProvider.kotlinScopeProvider, diagnosticsReporter) val shouldCountLines = (reportFilesAndLines != null) var linesCount = 0 val firFiles = files.map { file -> @@ -82,8 +83,9 @@ fun buildResolveAndCheckFirViaLightTree( session: FirSession, ktFiles: Collection, diagnosticsReporter: BaseDiagnosticsCollector, - countFilesAndLines: KFunction2? + countFilesAndLines: KFunction2?, + useHeaderCompilation: Boolean, ): ModuleCompilerAnalyzedOutput { - val firFiles = session.buildFirViaLightTree(ktFiles, diagnosticsReporter, countFilesAndLines) + val firFiles = session.buildFirViaLightTree(ktFiles, diagnosticsReporter, countFilesAndLines, useHeaderCompilation) return resolveAndCheckFir(session, firFiles, diagnosticsReporter) } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/LightTree2Fir.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/LightTree2Fir.kt index 47a1fc1d75e43..a919500b3f64d 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/LightTree2Fir.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/LightTree2Fir.kt @@ -23,6 +23,7 @@ import java.nio.file.Path class LightTree2Fir( val session: FirSession, + val headerCompilationMode: Boolean, private val scopeProvider: FirScopeProvider, private val diagnosticsReporter: DiagnosticReporter? = null, ) { @@ -43,7 +44,7 @@ class LightTree2Fir( sourceFile: KtSourceFile, linesMapping: KtSourceFileLinesMapping, ): FirFile { - return LightTreeRawFirDeclarationBuilder(session, scopeProvider, lightTree) + return LightTreeRawFirDeclarationBuilder(session, scopeProvider, lightTree, headerCompilationMode) .convertFile(lightTree.root, sourceFile, linesMapping) } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt index 68048ed228835..0e52388cc1d82 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt @@ -53,6 +53,7 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes +import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.util.getChildren import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled @@ -61,10 +62,10 @@ class LightTreeRawFirDeclarationBuilder( session: FirSession, internal val baseScopeProvider: FirScopeProvider, tree: FlyweightCapableTreeStructure, + private val headerCompilationMode: Boolean = false, context: Context = Context(), ) : AbstractLightTreeRawFirBuilder(session, tree, context) { - - private val expressionConverter = LightTreeRawFirExpressionBuilder(session, tree, this, context) + private val expressionConverter = LightTreeRawFirExpressionBuilder(session, tree, this, headerCompilationMode, context) /** * [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile] @@ -96,7 +97,12 @@ class LightTreeRawFirDeclarationBuilder( } IMPORT_LIST -> importList += convertImportDirectives(child) CLASS -> firDeclarationList += convertClass(child) - FUN -> firDeclarationList += convertFunctionDeclaration(child) as FirDeclaration + FUN -> { + val functionDeclaration = convertFunctionDeclaration(child) + if (functionDeclaration != null) { + firDeclarationList += functionDeclaration as FirDeclaration + } + } KtNodeTypes.PROPERTY -> firDeclarationList += convertPropertyDeclaration(child) TYPEALIAS -> firDeclarationList += convertTypeAlias(child) OBJECT_DECLARATION -> firDeclarationList += convertClass(child) @@ -142,7 +148,12 @@ class LightTreeRawFirDeclarationBuilder( val firStatements = block.forEachChildrenReturnList { node, container -> when (node.tokenType) { CLASS, OBJECT_DECLARATION -> container += convertClass(node) as FirStatement - FUN -> container += convertFunctionDeclaration(node) + FUN -> { + val functionDeclaration = convertFunctionDeclaration(node) + if (functionDeclaration != null) { + container += functionDeclaration + } + } KtNodeTypes.PROPERTY -> container += convertPropertyDeclaration(node) as FirStatement DESTRUCTURING_DECLARATION -> container += convertDestructingDeclaration(node).toFirDestructingDeclaration(this, baseModuleData) @@ -938,7 +949,11 @@ class LightTreeRawFirDeclarationBuilder( when (node.tokenType) { ENUM_ENTRY -> container += convertEnumEntry(node, classWrapper!!) CLASS -> container += convertClass(node) - FUN -> container += convertFunctionDeclaration(node) as FirDeclaration + FUN -> { + val functionDeclaration = convertFunctionDeclaration(node) + if (functionDeclaration != null) + container += functionDeclaration as FirDeclaration + } KtNodeTypes.PROPERTY -> container += convertPropertyDeclaration(node, classWrapper) TYPEALIAS -> container += convertTypeAlias(node) OBJECT_DECLARATION -> container += convertClass(node) @@ -1238,6 +1253,7 @@ class LightTreeRawFirDeclarationBuilder( modifiers?.convertAnnotationsTo(annotations) typeParameters += constructorTypeParametersFromConstructedClass(classWrapper.classBuilder.typeParameters) valueParameters += firValueParameters.map { it.firValueParameter } + // TODO-HEADER-COMPILATION: Potentially remove this and produce an empty constructor body. val (body, contractDescription) = withForcedLocalContext { convertFunctionBody(block, null, allowLegacyContractDescription = true) } @@ -1759,6 +1775,10 @@ class LightTreeRawFirDeclarationBuilder( valueParameters += firValueParameters } val allowLegacyContractDescription = outerContractDescription == null + // TODO-HEADER-COMPILATION: Potentially remove getter/setter code. + // (headerCompilationMode) { + // buildEmptyExpressionBlock() to null + // } val bodyWithContractDescription = withForcedLocalContext { convertFunctionBody(block, expression, allowLegacyContractDescription) } @@ -1922,7 +1942,7 @@ class LightTreeRawFirDeclarationBuilder( /** * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseFunction */ - fun convertFunctionDeclaration(functionDeclaration: LighterASTNode): FirStatement { + fun convertFunctionDeclaration(functionDeclaration: LighterASTNode): FirStatement? { var modifiers: ModifierList? = null var identifier: String? = null var valueParametersList: LighterASTNode? = null @@ -1976,8 +1996,23 @@ class LightTreeRawFirDeclarationBuilder( else implicitType } + // TODO-HEADER-COMPILE: While it's a good idea to try to avoid + // producing private functions, this breaks when the function is + // decorated with @JvmStatic since it makes it visible to Java + // programs and causes symbol not found errors. + // Fix would be to check if the annotation is there and then skip. + /* + if (headerCompilationMode) { + if (calculatedModifiers.getVisibility() == Visibilities.Private) { + return null + } + } + */ + val receiverTypeCalculator = receiverTypeNode?.let { { convertType(it) } } val functionBuilder = if (isAnonymousFunction) { + if (headerCompilationMode) + return null FirAnonymousFunctionBuilder().apply { source = functionSource receiverParameter = receiverTypeCalculator?.let { createReceiverParameter(it, baseModuleData, functionSymbol) } @@ -2064,8 +2099,41 @@ class LightTreeRawFirDeclarationBuilder( } val allowLegacyContractDescription = outerContractDescription == null - val bodyWithContractDescription = withForcedLocalContext { - convertFunctionBody(block, expression, allowLegacyContractDescription) + + // TODO: HEADER-COMPILATION + // replacing method bodies with `return null!!` doesn't seem to be ideal since it's producing a rather + // long bytecode sequence: + // Code: + // 0: aconst_null + // 1: dup + // 2: invokestatic #18 // Method kotlin/jvm/internal/Intrinsics.checkNotNull:(Ljava/lang/Object;)V + // 5: pop + // 6: new #26 // class kotlin/KotlinNothingValueException + // 9: dup + // 10: invokespecial #27 // Method kotlin/KotlinNothingValueException."":()V + // 13: athrow + // + // Consider unconditionally throwing an exception instead. + fun buildReturnNullExclExclBlock() = ( + FirSingleExpressionBlock( + buildReturnExpression { + this.target = target + result = buildCheckNotNullCall { + argumentList = buildUnaryArgumentList( + buildLiteralExpression(null, ConstantValueKind.Null, null, setType = true) + ) + } + } + ) to null) + + // Block is null when you have an interface or abstract function declaration. + // In such cases, for header compile mode you shall not generate any code either. + val bodyWithContractDescription = if (headerCompilationMode && block != null) { + buildReturnNullExclExclBlock() + } else { + withForcedLocalContext { + convertFunctionBody(block, expression, allowLegacyContractDescription) + } } this.body = bodyWithContractDescription.first val contractDescription = outerContractDescription ?: bodyWithContractDescription.second diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt index 18d557f3341f1..07ce96b273a33 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt @@ -62,6 +62,7 @@ class LightTreeRawFirExpressionBuilder( session: FirSession, tree: FlyweightCapableTreeStructure, private val declarationBuilder: LightTreeRawFirDeclarationBuilder, + val headerCompilationMode: Boolean = false, // TODO: may be unnecessary context: Context = Context(), ) : AbstractLightTreeRawFirBuilder(session, tree, context) { @@ -128,7 +129,7 @@ class LightTreeRawFirExpressionBuilder( } /***** EXPRESSIONS *****/ - fun convertExpression(expression: LighterASTNode, errorReason: String): FirElement { + fun convertExpression(expression: LighterASTNode, errorReason: String): FirElement? { return when (expression.tokenType) { LAMBDA_EXPRESSION -> convertLambdaExpression(expression) BINARY_EXPRESSION -> convertBinaryExpression(expression) diff --git a/compiler/fir/raw-fir/light-tree2fir/testFixtures/org/jetbrains/kotlin/fir/lightTree/AbstractLightTree2FirConverterTestCase.kt b/compiler/fir/raw-fir/light-tree2fir/testFixtures/org/jetbrains/kotlin/fir/lightTree/AbstractLightTree2FirConverterTestCase.kt index d3e84da34f845..e4ee354688081 100644 --- a/compiler/fir/raw-fir/light-tree2fir/testFixtures/org/jetbrains/kotlin/fir/lightTree/AbstractLightTree2FirConverterTestCase.kt +++ b/compiler/fir/raw-fir/light-tree2fir/testFixtures/org/jetbrains/kotlin/fir/lightTree/AbstractLightTree2FirConverterTestCase.kt @@ -25,6 +25,7 @@ abstract class AbstractLightTree2FirConverterTestCase : AbstractRawFirBuilderTes val path = Paths.get(filePath) val firFile = LightTree2Fir( session = FirSessionFactoryHelper.createEmptySession(parseLanguageFeatures(path.readText())), + headerCompilationMode = false, scopeProvider = StubFirScopeProvider, diagnosticsReporter = null ).buildFirFile(path) diff --git a/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/TotalKotlinTest.kt b/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/TotalKotlinTest.kt index 4a0629f8ba2b1..b46457ef3faa3 100644 --- a/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/TotalKotlinTest.kt +++ b/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/TotalKotlinTest.kt @@ -56,6 +56,7 @@ class TotalKotlinTest : AbstractRawFirBuilderTestCase() { @OptIn(ObsoleteTestInfrastructure::class) val lightTreeConverter = LightTree2Fir( session = FirSessionFactoryHelper.createEmptySession(), + headerCompilationMode = false, scopeProvider = StubFirScopeProvider, diagnosticsReporter = null ) diff --git a/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/compare/TreesCompareTest.kt b/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/compare/TreesCompareTest.kt index 40927d0330fe2..0497c9476b354 100644 --- a/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/compare/TreesCompareTest.kt +++ b/compiler/fir/raw-fir/light-tree2fir/tests/org/jetbrains/kotlin/fir/lightTree/compare/TreesCompareTest.kt @@ -62,6 +62,7 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() { val lightTreeConverter = LightTree2Fir( session = session, + headerCompilationMode = false, scopeProvider = StubFirScopeProvider, diagnosticsReporter = null ) @@ -97,6 +98,7 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() { val lightTreeConverter = LightTree2Fir( session = session, + headerCompilationMode = false, scopeProvider = StubFirScopeProvider, diagnosticsReporter = null ) diff --git a/compiler/tests-compiler-utils/testFixtures/org/jetbrains/kotlin/fir/FirAnalyzerFacade.kt b/compiler/tests-compiler-utils/testFixtures/org/jetbrains/kotlin/fir/FirAnalyzerFacade.kt index 6bf4a5baebdea..c737d7c88ac94 100644 --- a/compiler/tests-compiler-utils/testFixtures/org/jetbrains/kotlin/fir/FirAnalyzerFacade.kt +++ b/compiler/tests-compiler-utils/testFixtures/org/jetbrains/kotlin/fir/FirAnalyzerFacade.kt @@ -38,7 +38,7 @@ class FirAnalyzerFacade( private fun buildRawFir() { if (firFiles != null) return firFiles = when (parser) { - FirParser.LightTree -> session.buildFirViaLightTree(lightTreeFiles, diagnosticReporterForLightTree, reportFilesAndLines = null) + FirParser.LightTree -> session.buildFirViaLightTree(lightTreeFiles, diagnosticReporterForLightTree, reportFilesAndLines = null, false) FirParser.Psi -> session.buildFirFromKtFiles(ktFiles) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt index 01a9ab2d83ee3..99b31d5ec3f6c 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirFrontend.kt @@ -8,6 +8,7 @@ import org.jetbrains.kotlin.cli.common.fir.FirDiagnosticsCompilerResultsReporter import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.headerCompilation import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector import org.jetbrains.kotlin.fir.* @@ -123,7 +124,7 @@ internal fun PhaseContext.firFrontendWithLightTree(input: KotlinCoreEnvironment) isCommonSource = { groupedSources.isCommonSourceForLt(it) }, fileBelongsToModule = { file, it -> groupedSources.fileBelongsToModuleForLt(file, it) }, buildResolveAndCheckFir = { session, files, diagnosticsReporter -> - buildResolveAndCheckFirViaLightTree(session, files, diagnosticsReporter, null) + buildResolveAndCheckFirViaLightTree(session, files, diagnosticsReporter, null, configuration.headerCompilation) }, ) }