From 953b461c53d3a20a98f537fa5cad8268a39d4316 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 15 Jan 2020 19:27:39 +0100 Subject: [PATCH] Add new compiler errors and flags when JVM compiles against JVM IR From now on, the old JVM backend will report an error by default when compiling against class files produced by the JVM IR backend. This is needed because we're not yet sure that the ABI generated by JVM IR is fully correct and do not want to land in a 2-dimensional compatibility situation where we'll need to consider twice more scenarios when introducing any breaking change in the language. This is generally OK since the JVM IR backend is still going to be experimental in 1.4. However, for purposes of users which _do_ need to compile something with the old backend against JVM IR, we provide two new compiler flags: * -Xallow-jvm-ir-dependencies -- allows to suppress the error when compiling with the old backend against JVM IR. * -Xir-binary-with-stable-api -- allows to mark the generated binaries as stable, when compiling anything with JVM IR, so that dependent modules will compile even with the old backend automatically. In this case, the author usually does not care for the generated ABI, or s/he ensures that it's consistent with the one expected by the old compiler with some external tools. Internally, this is implemented by storing two new flags in kotlin.Metadata: one tells if the class file was compiled with the JVM IR, and another tells if the class file is stable (in case it's compiled with JVM IR). Implementation is similar to the diagnostic reported by the pre-release dependency checker. --- .../kotlin/codegen/state/GenerationState.kt | 1 + .../arguments/K2JVMCompilerArguments.kt | 15 +++++++++++ .../messages/AnalyzerWithCompilerReport.kt | 22 ++++++++-------- .../jetbrains/kotlin/cli/jvm/jvmArguments.kt | 1 + .../kotlin/config/JVMConfigurationKeys.java | 3 +++ .../jetbrains/kotlin/config/AnalysisFlags.kt | 3 +++ .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../CompilerDeserializationConfiguration.kt | 2 ++ .../checkers/MissingDependencyClassChecker.kt | 3 +++ .../backend/jvm/codegen/ClassCodegen.kt | 14 ++++++++--- compiler/testData/cli/jvm/extraHelp.out | 3 +++ .../jvmIrAgainstJvmIr/library/a.kt | 5 ++++ .../jvmIrAgainstJvmIr/output.txt | 1 + .../jvmIrAgainstJvmIr/source.kt | 5 ++++ .../jvmIrAgainstOld/library/a.kt | 5 ++++ .../jvmIrAgainstOld/output.txt | 1 + .../jvmIrAgainstOld/source.kt | 5 ++++ .../oldAgainstJvmIr/library/a.kt | 5 ++++ .../oldAgainstJvmIr/output.txt | 8 ++++++ .../oldAgainstJvmIr/source.kt | 5 ++++ .../library/a.kt | 5 ++++ .../output.txt | 1 + .../source.kt | 5 ++++ .../oldAgainstJvmIrWithStableAbi/library/a.kt | 5 ++++ .../oldAgainstJvmIrWithStableAbi/output.txt | 1 + .../oldAgainstJvmIrWithStableAbi/source.kt | 5 ++++ .../CompileKotlinAgainstCustomBinariesTest.kt | 25 +++++++++++++++++++ .../kotlin/load/java/JvmAnnotationNames.java | 2 ++ .../kotlin/DeserializedDescriptorResolver.kt | 10 ++++++-- .../load/kotlin/JvmPackagePartSource.kt | 5 +++- .../kotlin/KotlinJvmBinarySourceElement.kt | 3 ++- .../load/kotlin/header/KotlinClassHeader.kt | 4 +++ .../DeserializationConfiguration.kt | 3 +++ .../DeserializedMemberDescriptor.kt | 4 +++ .../js/KotlinJavascriptPackageFragment.kt | 3 +++ .../stdlib/jvm/runtime/kotlin/Metadata.kt | 7 ++++-- 37 files changed, 177 insertions(+), 20 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/library/a.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstJvmIr/source.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/library/a.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmIrAgainstOld/source.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/library/a.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIr/source.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/library/a.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithAllowIrDependencies/source.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/library/a.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/oldAgainstJvmIrWithStableAbi/source.kt 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")