diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 99cf3ea4304..d086176a249 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -264,6 +264,7 @@ class GenerationState private constructor( val disableOptimization = configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false) val metadataVersion = configuration.get(CommonConfigurationKeys.METADATA_VERSION) ?: JvmMetadataVersion.INSTANCE + val isIrWithStableAbi = configuration.getBoolean(JVMConfigurationKeys.IS_IR_WITH_STABLE_ABI) val globalSerializationBindings = JvmSerializationBindings() lateinit var irBasedMapAsmMethod: (FunctionDescriptor) -> Method diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index 223edfca474..923e04605df 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -93,6 +93,20 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { ) var irCheckLocalNames: Boolean by FreezableVar(false) + @Argument( + value = "-Xallow-jvm-ir-dependencies", + description = "When not using the IR backend, do not report errors on those classes in dependencies, " + + "which were compiled by the IR backend" + ) + var allowJvmIrDependencies: Boolean by FreezableVar(false) + + @Argument( + value = "-Xir-binary-with-stable-abi", + description = "When using the IR backend, produce binaries which can be read by non-IR backend.\n" + + "The author is responsible for verifying that the resulting binaries do indeed have the correct ABI" + ) + var isIrWithStableAbi: Boolean by FreezableVar(false) + @Argument(value = "-Xmodule-path", valueDescription = "", description = "Paths where to find Java 9+ modules") var javaModulePath: String? by NullableStringFreezableVar(null) @@ -327,6 +341,7 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { result[JvmAnalysisFlags.sanitizeParentheses] = sanitizeParentheses result[JvmAnalysisFlags.suppressMissingBuiltinsError] = suppressMissingBuiltinsError result[JvmAnalysisFlags.irCheckLocalNames] = irCheckLocalNames + result[AnalysisFlags.reportErrorsOnIrDependencies] = !useIR && !useFir && !allowJvmIrDependencies return result } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt index 2e8db2c0294..000d19b45c3 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt @@ -146,25 +146,19 @@ class AnalyzerWithCompilerReport( return diagnostic.severity == Severity.ERROR } - data class ReportDiagnosticsResult(val hasErrors: Boolean, val hasIncompatibleClassErrors: Boolean) - - fun reportDiagnostics(unsortedDiagnostics: Diagnostics, reporter: DiagnosticMessageReporter): ReportDiagnosticsResult { + fun reportDiagnostics(unsortedDiagnostics: Diagnostics, reporter: DiagnosticMessageReporter): Boolean { var hasErrors = false - var hasIncompatibleClassErrors = false val diagnostics = sortedDiagnostics(unsortedDiagnostics.all()) for (diagnostic in diagnostics) { hasErrors = hasErrors or reportDiagnostic(diagnostic, reporter) - hasIncompatibleClassErrors = hasIncompatibleClassErrors or - (diagnostic.factory == Errors.INCOMPATIBLE_CLASS || diagnostic.factory == Errors.PRE_RELEASE_CLASS) } - - return ReportDiagnosticsResult(hasErrors, hasIncompatibleClassErrors) + return hasErrors } fun reportDiagnostics(diagnostics: Diagnostics, messageCollector: MessageCollector): Boolean { - val (hasErrors, hasIncompatibleClassErrors) = reportDiagnostics(diagnostics, DefaultDiagnosticReporter(messageCollector)) + val hasErrors = reportDiagnostics(diagnostics, DefaultDiagnosticReporter(messageCollector)) - if (hasIncompatibleClassErrors) { + if (diagnostics.any { it.factory == Errors.INCOMPATIBLE_CLASS || it.factory == Errors.PRE_RELEASE_CLASS }) { messageCollector.report( ERROR, "Incompatible classes were found in dependencies. " + @@ -172,6 +166,14 @@ class AnalyzerWithCompilerReport( ) } + if (diagnostics.any { it.factory == Errors.IR_COMPILED_CLASS }) { + messageCollector.report( + ERROR, + "Classes compiled by a new Kotlin compiler backend were found in dependencies. " + + "Remove them from the classpath or use '-Xallow-jvm-ir-dependencies' to suppress errors" + ) + } + return hasErrors } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt index 523f0742c42..a3f17c18529 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt @@ -143,6 +143,7 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr put(JVMConfigurationKeys.PARAMETERS_METADATA, arguments.javaParameters) put(JVMConfigurationKeys.IR, arguments.useIR && !arguments.noUseIR) + put(JVMConfigurationKeys.IS_IR_WITH_STABLE_ABI, arguments.isIrWithStableAbi) put(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, arguments.noCallAssertions) put(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS, arguments.noReceiverAssertions) put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java b/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java index 8e7a4a6671f..4257128f186 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java @@ -116,4 +116,7 @@ public class JVMConfigurationKeys { public static final CompilerConfigurationKey> KLIB_PATHS = CompilerConfigurationKey.create("Paths to .klib libraries"); + + public static final CompilerConfigurationKey IS_IR_WITH_STABLE_ABI = + CompilerConfigurationKey.create("Is IR with stable ABI"); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/config/AnalysisFlags.kt b/compiler/frontend/src/org/jetbrains/kotlin/config/AnalysisFlags.kt index 8ee962b13ee..2959d8ccd2a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/config/AnalysisFlags.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/config/AnalysisFlags.kt @@ -41,4 +41,7 @@ object AnalysisFlags { @JvmStatic val ideMode by AnalysisFlag.Delegates.Boolean + + @JvmStatic + val reportErrorsOnIrDependencies by AnalysisFlag.Delegates.Boolean } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index c24073924a1..c196b5cfa98 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -109,6 +109,7 @@ public interface Errors { DiagnosticFactory1 MISSING_IMPORTED_SCRIPT_PSI = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 MISSING_SCRIPT_PROVIDED_PROPERTY_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 PRE_RELEASE_CLASS = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 IR_COMPILED_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory2> INCOMPATIBLE_CLASS = DiagnosticFactory2.create(ERROR); //Elements with "INVISIBLE_REFERENCE" error are marked as unresolved, unlike elements with "INVISIBLE_MEMBER" error diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index f3a131903cd..31b9edd30c3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -384,6 +384,7 @@ public class DefaultErrorMessages { MAP.put(MISSING_IMPORTED_SCRIPT_PSI, "Imported script file ''{0}'' is not loaded. Check your script imports", TO_STRING); MAP.put(MISSING_SCRIPT_PROVIDED_PROPERTY_CLASS, "Cannot access script provided property class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING); MAP.put(PRE_RELEASE_CLASS, "{0} is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler", TO_STRING); + MAP.put(IR_COMPILED_CLASS, "{0} is compiled by a new Kotlin compiler backend and cannot be loaded by the old compiler", TO_STRING); MAP.put(INCOMPATIBLE_CLASS, "{0} was compiled with an incompatible version of Kotlin. {1}", TO_STRING, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompilerDeserializationConfiguration.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompilerDeserializationConfiguration.kt index d10d9e3738c..217452b661b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompilerDeserializationConfiguration.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompilerDeserializationConfiguration.kt @@ -17,6 +17,8 @@ class CompilerDeserializationConfiguration(languageVersionSettings: LanguageVers override val reportErrorsOnPreReleaseDependencies = !skipMetadataVersionCheck && !languageVersionSettings.isPreRelease() && !KotlinCompilerVersion.isPreRelease() + override val reportErrorsOnIrDependencies = languageVersionSettings.getFlag(AnalysisFlags.reportErrorsOnIrDependencies) + override val typeAliasesAllowed = languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases) override val isJvmPackageNameSupported = languageVersionSettings.supportsFeature(LanguageFeature.JvmPackageName) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/MissingDependencyClassChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/MissingDependencyClassChecker.kt index 69ac3096801..b1f6bcd4ef5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/MissingDependencyClassChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/MissingDependencyClassChecker.kt @@ -58,6 +58,9 @@ object MissingDependencyClassChecker : CallChecker { if (source.isPreReleaseInvisible) { return PRE_RELEASE_CLASS.on(reportOn, source.presentableString) } + if (source.isInvisibleIrDependency) { + return IR_COMPILED_CLASS.on(reportOn, source.presentableString) + } } return null diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 9ce550328cd..cf12f809525 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -216,10 +216,16 @@ open class ClassCodegen protected constructor( state.bindingTrace.record(CodegenBinding.DELEGATED_PROPERTIES_WITH_METADATA, type, localDelegatedProperties.map { it.descriptor }) } + // TODO: if `-Xmultifile-parts-inherit` is enabled, write the corresponding flag for parts and facades to [Metadata.extraInt]. + var extraFlags = JvmAnnotationNames.METADATA_JVM_IR_FLAG + if (state.isIrWithStableAbi) { + extraFlags += JvmAnnotationNames.METADATA_JVM_IR_STABLE_ABI_FLAG + } + when (val metadata = irClass.metadata) { is MetadataSource.Class -> { val classProto = serializer!!.classProto(metadata.descriptor).build() - writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.CLASS, 0) { + writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.CLASS, extraFlags) { AsmUtil.writeAnnotationData(it, serializer, classProto) } @@ -235,7 +241,7 @@ open class ClassCodegen protected constructor( val facadeClassName = context.multifileFacadeForPart[irClass.attributeOwnerId] val kind = if (facadeClassName != null) KotlinClassHeader.Kind.MULTIFILE_CLASS_PART else KotlinClassHeader.Kind.FILE_FACADE - writeKotlinMetadata(visitor, state, kind, 0) { av -> + writeKotlinMetadata(visitor, state, kind, extraFlags) { av -> AsmUtil.writeAnnotationData(av, serializer, packageProto.build()) if (facadeClassName != null) { @@ -250,7 +256,7 @@ open class ClassCodegen protected constructor( is MetadataSource.Function -> { val fakeDescriptor = createFreeFakeLambdaDescriptor(metadata.descriptor) val functionProto = serializer!!.functionProto(fakeDescriptor)?.build() - writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, 0) { + writeKotlinMetadata(visitor, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, extraFlags) { if (functionProto != null) { AsmUtil.writeAnnotationData(it, serializer, functionProto) } @@ -264,7 +270,7 @@ open class ClassCodegen protected constructor( if (fileClass != null) typeMapper.mapClass(fileClass).internalName else null } MultifileClassCodegenImpl.writeMetadata( - visitor, state, 0 /* TODO */, partInternalNames, type, irClass.fqNameWhenAvailable!!.parent() + visitor, state, extraFlags, partInternalNames, type, irClass.fqNameWhenAvailable!!.parent() ) } else { writeSyntheticClassMetadata(visitor, state) diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index 511c5075161..5146b25f7b3 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -2,6 +2,7 @@ Usage: kotlinc-jvm where advanced options include: -Xadd-modules= Root modules to resolve in addition to the initial modules, or all modules on the module path if is ALL-MODULE-PATH + -Xallow-jvm-ir-dependencies When not using the IR backend, do not report errors on those classes in dependencies, which were compiled by the IR backend -Xallow-no-source-files Allow no source files -Xassertions={always-enable|always-disable|jvm|legacy} Assert calls behaviour @@ -22,6 +23,8 @@ where advanced options include: -Xfriend-paths= Paths to output directories for friend modules (whose internals should be visible) -Xmultifile-parts-inherit Compile multifile classes as a hierarchy of parts and facade -Xir-check-local-names Check that names of local classes and anonymous objects are the same in the IR backend as in the old backend + -Xir-binary-with-stable-abi When using the IR backend, produce binaries which can be read by non-IR backend. + The author is responsible for verifying that the resulting binaries do indeed have the correct ABI -Xmodule-path= Paths where to find Java 9+ modules -Xjava-package-prefix Package prefix for Java files -Xjava-source-roots= Paths to directories with Java source files diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/library/a.kt new file mode 100644 index 00000000000..71e3e5d293d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/library/a.kt @@ -0,0 +1,5 @@ +package lib + +class Box(val value: String) + +inline fun get(block: () -> T): T = block() diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/source.kt new file mode 100644 index 00000000000..ffadd735100 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/source.kt @@ -0,0 +1,5 @@ +import lib.* + +fun main() { + get { Box("OK").value } +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/library/a.kt new file mode 100644 index 00000000000..71e3e5d293d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/library/a.kt @@ -0,0 +1,5 @@ +package lib + +class Box(val value: String) + +inline fun get(block: () -> T): T = block() diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/source.kt new file mode 100644 index 00000000000..ffadd735100 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/source.kt @@ -0,0 +1,5 @@ +import lib.* + +fun main() { + get { Box("OK").value } +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/library/a.kt new file mode 100644 index 00000000000..71e3e5d293d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/library/a.kt @@ -0,0 +1,5 @@ +package lib + +class Box(val value: String) + +inline fun get(block: () -> T): T = block() diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/output.txt new file mode 100644 index 00000000000..6bde2a37585 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/output.txt @@ -0,0 +1,8 @@ +error: classes compiled by a new Kotlin compiler backend were found in dependencies. Remove them from the classpath or use '-Xallow-jvm-ir-dependencies' to suppress errors +compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/source.kt:4:5: error: class 'lib.AKt' is compiled by a new Kotlin compiler backend and cannot be loaded by the old compiler + get { Box("OK").value } + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/source.kt:4:11: error: class 'lib.Box' is compiled by a new Kotlin compiler backend and cannot be loaded by the old compiler + get { Box("OK").value } + ^ +COMPILATION_ERROR diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/source.kt new file mode 100644 index 00000000000..ffadd735100 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/source.kt @@ -0,0 +1,5 @@ +import lib.* + +fun main() { + get { Box("OK").value } +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/library/a.kt new file mode 100644 index 00000000000..71e3e5d293d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/library/a.kt @@ -0,0 +1,5 @@ +package lib + +class Box(val value: String) + +inline fun get(block: () -> T): T = block() diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/source.kt new file mode 100644 index 00000000000..ffadd735100 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/source.kt @@ -0,0 +1,5 @@ +import lib.* + +fun main() { + get { Box("OK").value } +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/library/a.kt new file mode 100644 index 00000000000..71e3e5d293d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/library/a.kt @@ -0,0 +1,5 @@ +package lib + +class Box(val value: String) + +inline fun get(block: () -> T): T = block() diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/source.kt new file mode 100644 index 00000000000..ffadd735100 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/source.kt @@ -0,0 +1,5 @@ +import lib.* + +fun main() { + get { Box("OK").value } +} diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt index 5afffb0414a..c3970904281 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt @@ -629,6 +629,31 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration classLoader.loadClass("SourceKt").getDeclaredMethod("main").invoke(null) } + fun testJvmIrAgainstJvmIr() { + val library = compileLibrary("library", additionalOptions = listOf("-Xuse-ir")) + compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-Xuse-ir")) + } + + fun testJvmIrAgainstOld() { + val library = compileLibrary("library") + compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-Xuse-ir")) + } + + fun testOldAgainstJvmIr() { + val library = compileLibrary("library", additionalOptions = listOf("-Xuse-ir")) + compileKotlin("source.kt", tmpdir, listOf(library)) + } + + fun testOldAgainstJvmIrWithStableAbi() { + val library = compileLibrary("library", additionalOptions = listOf("-Xuse-ir", "-Xir-binary-with-stable-abi")) + compileKotlin("source.kt", tmpdir, listOf(library)) + } + + fun testOldAgainstJvmIrWithAllowIrDependencies() { + val library = compileLibrary("library", additionalOptions = listOf("-Xuse-ir")) + compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-Xallow-jvm-ir-dependencies")) + } + companion object { // compiler before 1.1.4 version did not include suspension marks into bytecode. private fun stripSuspensionMarksToImitateLegacyCompiler(bytes: ByteArray): Pair { diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java index 5b366c9df35..c4d3f0e7243 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java @@ -39,6 +39,8 @@ public final class JvmAnnotationNames { public static final int METADATA_PRE_RELEASE_FLAG = 1 << 1; public static final int METADATA_SCRIPT_FLAG = 1 << 2; public static final int METADATA_STRICT_VERSION_SEMANTICS_FLAG = 1 << 3; + public static final int METADATA_JVM_IR_FLAG = 1 << 4; + public static final int METADATA_JVM_IR_STABLE_ABI_FLAG = 1 << 5; public static final Name DEFAULT_ANNOTATION_MEMBER_NAME = Name.identifier("value"); diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/DeserializedDescriptorResolver.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/DeserializedDescriptorResolver.kt index eb3a5ba5377..053837be1d7 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/DeserializedDescriptorResolver.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/DeserializedDescriptorResolver.kt @@ -52,7 +52,9 @@ class DeserializedDescriptorResolver { val (nameResolver, classProto) = parseProto(kotlinClass) { JvmProtoBufUtil.readClassDataFrom(data, strings) } ?: return null - val source = KotlinJvmBinarySourceElement(kotlinClass, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible) + val source = KotlinJvmBinarySourceElement( + kotlinClass, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible, kotlinClass.isInvisibleJvmIrDependency + ) return ClassData(nameResolver, classProto, kotlinClass.classHeader.metadataVersion, source) } @@ -63,7 +65,8 @@ class DeserializedDescriptorResolver { JvmProtoBufUtil.readPackageDataFrom(data, strings) } ?: return null val source = JvmPackagePartSource( - kotlinClass, packageProto, nameResolver, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible + kotlinClass, packageProto, nameResolver, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible, + kotlinClass.isInvisibleJvmIrDependency ) return DeserializedPackageMemberScope( descriptor, packageProto, nameResolver, kotlinClass.classHeader.metadataVersion, source, components @@ -94,6 +97,9 @@ class DeserializedDescriptorResolver { get() = !components.configuration.skipMetadataVersionCheck && classHeader.isPreRelease && classHeader.metadataVersion == KOTLIN_1_3_M1_METADATA_VERSION + private val KotlinJvmBinaryClass.isInvisibleJvmIrDependency: Boolean + get() = components.configuration.reportErrorsOnIrDependencies && classHeader.isUnstableJvmIrBinary + private fun readData(kotlinClass: KotlinJvmBinaryClass, expectedKinds: Set): Array? { val header = kotlinClass.classHeader return (header.data ?: header.incompatibleData)?.takeIf { header.kind in expectedKinds } diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartSource.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartSource.kt index 9c2221843a9..54220ad7061 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartSource.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartSource.kt @@ -36,6 +36,7 @@ class JvmPackagePartSource( nameResolver: NameResolver, override val incompatibility: IncompatibleVersionErrorData? = null, override val isPreReleaseInvisible: Boolean = false, + override val isInvisibleIrDependency: Boolean = false, val knownJvmBinaryClass: KotlinJvmBinaryClass? = null ) : DeserializedContainerSource { constructor( @@ -43,7 +44,8 @@ class JvmPackagePartSource( packageProto: ProtoBuf.Package, nameResolver: NameResolver, incompatibility: IncompatibleVersionErrorData? = null, - isPreReleaseInvisible: Boolean = false + isPreReleaseInvisible: Boolean = false, + isInvisibleIrDependency: Boolean = false ) : this( JvmClassName.byClassId(kotlinClass.classId), kotlinClass.classHeader.multifileClassName?.let { @@ -53,6 +55,7 @@ class JvmPackagePartSource( nameResolver, incompatibility, isPreReleaseInvisible, + isInvisibleIrDependency, kotlinClass ) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmBinarySourceElement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmBinarySourceElement.kt index 229515f0117..9d088888b7a 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmBinarySourceElement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmBinarySourceElement.kt @@ -24,7 +24,8 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ class KotlinJvmBinarySourceElement( val binaryClass: KotlinJvmBinaryClass, override val incompatibility: IncompatibleVersionErrorData? = null, - override val isPreReleaseInvisible: Boolean = false + override val isPreReleaseInvisible: Boolean = false, + override val isInvisibleIrDependency: Boolean = false ) : DeserializedContainerSource { override val presentableString: String get() = "Class '${binaryClass.classId.asSingleFqName().asString()}'" diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/header/KotlinClassHeader.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/header/KotlinClassHeader.kt index 90d138276ce..98b668ac5ce 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/header/KotlinClassHeader.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/header/KotlinClassHeader.kt @@ -71,6 +71,10 @@ class KotlinClassHeader( DELEGATING } else null + val isUnstableJvmIrBinary: Boolean + get() = (extraInt and JvmAnnotationNames.METADATA_JVM_IR_FLAG) != 0 && + (extraInt and JvmAnnotationNames.METADATA_JVM_IR_STABLE_ABI_FLAG == 0) + val isPreRelease: Boolean get() = (extraInt and JvmAnnotationNames.METADATA_PRE_RELEASE_FLAG) != 0 diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializationConfiguration.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializationConfiguration.kt index cc175083159..a7748582dbb 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializationConfiguration.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializationConfiguration.kt @@ -12,6 +12,9 @@ interface DeserializationConfiguration { val reportErrorsOnPreReleaseDependencies: Boolean get() = false + val reportErrorsOnIrDependencies: Boolean + get() = false + val typeAliasesAllowed: Boolean get() = true diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt index 3f62b928b8d..dffde946018 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt @@ -52,6 +52,10 @@ interface DeserializedContainerSource : SourceElement { // True iff this is container is "invisible" because it's loaded from a pre-release class and this compiler is a release val isPreReleaseInvisible: Boolean + // True iff this container was compiled by the new IR backend, this compiler is not using the IR backend right now, + // and no additional flags to override this behavior were specified. + val isInvisibleIrDependency: Boolean + // This string should only be used in error messages val presentableString: String } diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptPackageFragment.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptPackageFragment.kt index eafaa6bc15e..484abea58c7 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptPackageFragment.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptPackageFragment.kt @@ -94,6 +94,9 @@ class KotlinJavascriptPackageFragment( override val isPreReleaseInvisible: Boolean = configuration.reportErrorsOnPreReleaseDependencies && (header.flags and 1) != 0 + override val isInvisibleIrDependency: Boolean + get() = false + override val presentableString: String get() = "Package '$fqName'" } diff --git a/libraries/stdlib/jvm/runtime/kotlin/Metadata.kt b/libraries/stdlib/jvm/runtime/kotlin/Metadata.kt index f8010aec16f..c39338e5612 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/Metadata.kt +++ b/libraries/stdlib/jvm/runtime/kotlin/Metadata.kt @@ -42,7 +42,7 @@ public annotation class Metadata( @get:JvmName("d1") val data1: Array = [], /** - * An addition to [d1]: array of strings which occur in metadata, written in plain text so that strings already present + * An addition to [data1]: array of strings which occur in metadata, written in plain text so that strings already present * in the constant pool are reused. These strings may be then indexed in the metadata by an integer index in this array. */ @get:JvmName("d2") @@ -67,7 +67,10 @@ public annotation class Metadata( * * 1 - this class file is compiled by a pre-release version of Kotlin and is not visible to release versions. * * 2 - this class file is a compiled Kotlin script source file (.kts). * * 3 - the metadata of this class file is not supposed to be read by the compiler, whose major.minor version is less than - * the major.minor version of this metadata ([mv]). + * the major.minor version of this metadata ([metadataVersion]). + * * 4 - this class file is compiled with the new Kotlin compiler backend introduced in Kotlin 1.4. + * * 5 - if the class file is compiled with the new Kotlin compiler backend, the metadata has been verified by the author and + * no metadata incompatibility diagnostic should be reported at the call site. */ @SinceKotlin("1.1") @get:JvmName("xi")