diff --git a/build-common/src/org/jetbrains/kotlin/build/BuildMetaInfo.kt b/build-common/src/org/jetbrains/kotlin/build/BuildMetaInfo.kt index 8d051eb39f7..f6872c701bc 100644 --- a/build-common/src/org/jetbrains/kotlin/build/BuildMetaInfo.kt +++ b/build-common/src/org/jetbrains/kotlin/build/BuildMetaInfo.kt @@ -6,10 +6,7 @@ package org.jetbrains.kotlin.build import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments -import org.jetbrains.kotlin.config.ApiVersion -import org.jetbrains.kotlin.config.LanguageVersion -import org.jetbrains.kotlin.config.PluginClasspaths -import org.jetbrains.kotlin.config.PluginClasspathsComparator +import org.jetbrains.kotlin.config.* abstract class BuildMetaInfo { enum class CustomKeys { @@ -74,7 +71,7 @@ abstract class BuildMetaInfo { val languageVersionSting = languageVersion.versionString resultMap[CustomKeys.LANGUAGE_VERSION_STRING.name] = languageVersionSting - val isEAP = !languageVersion.isStable + val isEAP = languageVersion.isPreRelease() resultMap[CustomKeys.IS_EAP.name] = isEAP.toString() val apiVersionString = args.apiVersion ?: languageVersionSting diff --git a/compiler/compiler.version/src/org/jetbrains/kotlin/config/KotlinCompilerVersion.java b/compiler/compiler.version/src/org/jetbrains/kotlin/config/KotlinCompilerVersion.java index 5c50d2c23aa..e7b25336582 100644 --- a/compiler/compiler.version/src/org/jetbrains/kotlin/config/KotlinCompilerVersion.java +++ b/compiler/compiler.version/src/org/jetbrains/kotlin/config/KotlinCompilerVersion.java @@ -15,6 +15,23 @@ public class KotlinCompilerVersion { public static final String VERSION_FILE_PATH = "/META-INF/compiler.version"; public static final String VERSION; + // True if the latest stable language version supported by this compiler has not yet been released. + // Binaries produced by this compiler with that language version (or any future language version) are going to be marked + // as "pre-release" and will not be loaded by release versions of the compiler. + // Change this value before and after every major release + private static final boolean IS_PRE_RELEASE = false; + + public static final String TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY = "kotlin.test.is.pre.release"; + + public static boolean isPreRelease() { + String overridden = System.getProperty(TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY); + if (overridden != null) { + return Boolean.parseBoolean(overridden); + } + + return IS_PRE_RELEASE; + } + /** * @return version of this compiler, or `null` if it isn't known (if VERSION is "@snapshot@") */ @@ -41,5 +58,12 @@ public class KotlinCompilerVersion { catch (IOException e) { throw new IllegalStateException("Failed to read compiler version from " + VERSION_FILE_PATH); } + + if (!VERSION.equals("@snapshot@") && !VERSION.contains("-") && IS_PRE_RELEASE) { + throw new IllegalStateException( + "IS_PRE_RELEASE cannot be true for a compiler without '-' in its version.\n" + + "Please change IS_PRE_RELEASE to false, commit and push this change to master" + ); + } } } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt index 471ee9ff8a9..a40f026c634 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.java.deserialization import org.jetbrains.kotlin.config.AnalysisFlags +import org.jetbrains.kotlin.config.KotlinCompilerVersion import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.ThreadSafeMutableState @@ -60,8 +61,9 @@ class JvmClassFileBasedSymbolProvider( private val annotationsLoader = AnnotationsLoader(session, kotlinClassFinder) private val ownMetadataVersion: JvmMetadataVersion = session.languageVersionSettings.languageVersion.toMetadataVersion() - private val reportErrorsOnPreReleaseDependencies = - !session.languageVersionSettings.getFlag(AnalysisFlags.skipPrereleaseCheck) && !session.languageVersionSettings.isPreRelease() + private val reportErrorsOnPreReleaseDependencies = with(session.languageVersionSettings) { + !getFlag(AnalysisFlags.skipPrereleaseCheck) && !isPreRelease() && !KotlinCompilerVersion.isPreRelease() + } override fun computePackagePartsInfos(packageFqName: FqName): List = packagePartProvider.findPackageParts(packageFqName.asString()).mapNotNull { partName -> diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompilerDeserializationConfiguration.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompilerDeserializationConfiguration.kt index e15d1afb274..192e5d50670 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompilerDeserializationConfiguration.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CompilerDeserializationConfiguration.kt @@ -5,7 +5,10 @@ package org.jetbrains.kotlin.resolve -import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.config.AnalysisFlags +import org.jetbrains.kotlin.config.KotlinCompilerVersion +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration open class CompilerDeserializationConfiguration( @@ -17,7 +20,7 @@ open class CompilerDeserializationConfiguration( final override val skipPrereleaseCheck = languageVersionSettings.getFlag(AnalysisFlags.skipPrereleaseCheck) final override val reportErrorsOnPreReleaseDependencies = - !skipPrereleaseCheck && !languageVersionSettings.isPreRelease() + !skipPrereleaseCheck && !languageVersionSettings.isPreRelease() && !KotlinCompilerVersion.isPreRelease() final override val allowUnstableDependencies = languageVersionSettings.getFlag(AnalysisFlags.allowUnstableDependencies) diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/library/a.kt new file mode 100644 index 00000000000..b1828248591 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/library/a.kt @@ -0,0 +1,5 @@ +package a + +class A + +fun A.foo() = "" diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/source.kt new file mode 100644 index 00000000000..298d363e749 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryLatestStable/source.kt @@ -0,0 +1,7 @@ +package usage + +import a.* + +fun baz(param: A) { + param.foo() +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/library/a.kt new file mode 100644 index 00000000000..dbf30478826 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/library/a.kt @@ -0,0 +1,5 @@ +package a + +class A + +fun foo() = "" diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/source.kt new file mode 100644 index 00000000000..f21b3de444d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/preReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion/source.kt @@ -0,0 +1,8 @@ +package usage + +import a.* + +fun baz(param: A): A { + foo() + return param +} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/CompilerTestLanguageVersionSettings.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/CompilerTestLanguageVersionSettings.kt index 7e0d8fc655f..e9021e43350 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/CompilerTestLanguageVersionSettings.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/CompilerTestLanguageVersionSettings.kt @@ -37,7 +37,7 @@ data class CompilerTestLanguageVersionSettings( override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State = extraLanguageFeatures[feature] ?: delegate.getFeatureSupport(feature) - override fun isPreRelease(): Boolean = false + override fun isPreRelease(): Boolean = KotlinCompilerVersion.isPreRelease() @Suppress("UNCHECKED_CAST") override fun getFlag(flag: AnalysisFlag): T = analysisFlags[flag] as T? ?: flag.defaultValue diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt index 33c67bfb140..19ffa4684e7 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.jvm.compiler import com.intellij.openapi.util.io.FileUtil +import junit.framework.TestCase import org.jetbrains.kotlin.cli.common.CLICompiler import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.js.K2JSCompiler @@ -16,6 +17,8 @@ import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler import org.jetbrains.kotlin.cli.transformMetadataInClassFile import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.config.KotlinCompilerVersion +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.incremental.LocalFileKotlinClass import org.jetbrains.kotlin.load.java.JvmAnnotationNames @@ -25,6 +28,7 @@ import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.MockLibraryUtil import org.jetbrains.kotlin.test.TestJdkKind +import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.toMetadataVersion import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.tree.ClassNode @@ -128,6 +132,14 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo compileKotlin("source.kt", usageDestination, listOf(result), compiler, additionalOptions.toList()) } + private fun withPreRelease(block: () -> T): T = + try { + System.setProperty(KotlinCompilerVersion.TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY, "true") + block() + } finally { + System.clearProperty(KotlinCompilerVersion.TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY) + } + // ------------------------------------------------------------------------------ // KT-62043 @@ -265,6 +277,31 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo doTestPreReleaseKotlinLibrary(K2JVMCompiler(), "library", tmpdir, "-Xskip-metadata-version-check") } + fun testPreReleaseCompilerAgainstPreReleaseLibraryStableLanguageVersion() { + withPreRelease { + val library = compileLibrary("library") + val someStableReleasedVersion = LanguageVersion.entries.first { it.isStable && it >= LanguageVersion.FIRST_NON_DEPRECATED } + compileKotlin( + "source.kt", tmpdir, listOf(library), K2JVMCompiler(), + listOf("-language-version", someStableReleasedVersion.versionString) + ) + + checkPreReleaseness(File(tmpdir, "usage/SourceKt.class"), shouldBePreRelease = false) + } + } + + fun testPreReleaseCompilerAgainstPreReleaseLibraryLatestStable() { + withPreRelease { + val library = compileLibrary("library") + compileKotlin( + "source.kt", tmpdir, listOf(library), K2JVMCompiler(), + listOf("-language-version", LanguageVersion.LATEST_STABLE.versionString) + ) + + checkPreReleaseness(File(tmpdir, "usage/SourceKt.class"), shouldBePreRelease = true) + } + } + fun testReleaseCompilerAgainstPreReleaseLibrarySkipPrereleaseCheckAllowUnstableDependencies() { doTestPreReleaseKotlinLibrary(K2JVMCompiler(), "library", tmpdir, "-Xallow-unstable-dependencies", "-Xskip-prerelease-check") } @@ -671,6 +708,25 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo assertEquals("Output:\n$output", ExitCode.COMPILATION_ERROR, exitCode) } + // If this test fails, then bootstrap compiler most likely should be advanced + fun testPreReleaseFlagIsConsistentBetweenBootstrapAndCurrentCompiler() { + val bootstrapCompiler = JarFile(PathUtil.kotlinPathsForCompiler.compilerPath) + val classFromBootstrapCompiler = bootstrapCompiler.getEntry(LanguageFeature::class.java.name.replace(".", "/") + ".class") + checkPreReleaseness( + bootstrapCompiler.getInputStream(classFromBootstrapCompiler).readBytes(), + KotlinCompilerVersion.isPreRelease() + ) + } + + fun testPreReleaseFlagIsConsistentBetweenStdlibAndCurrentCompiler() { + val stdlib = JarFile(PathUtil.kotlinPathsForCompiler.stdlibPath) + val classFromStdlib = stdlib.getEntry(KotlinVersion::class.java.name.replace(".", "/") + ".class") + checkPreReleaseness( + stdlib.getInputStream(classFromStdlib).readBytes(), + KotlinCompilerVersion.isPreRelease() + ) + } + fun testAnonymousObjectTypeMetadata() { val library = compileCommonLibrary( libraryName = "library", @@ -696,7 +752,7 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo listOf("-Xmetadata-klib") ) } - + companion object { @JvmStatic protected fun copyJarFileWithoutEntry(jarPath: File, vararg entriesToDelete: String): File = @@ -729,4 +785,29 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo return outputFile } } + + private fun checkPreReleaseness(classFileBytes: ByteArray, shouldBePreRelease: Boolean) { + // If there's no "xi" field in the Metadata annotation, it's value is assumed to be 0, i.e. _not_ pre-release + var isPreRelease = false + + ClassReader(classFileBytes).accept(object : ClassVisitor(Opcodes.API_VERSION) { + override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? { + if (desc != JvmAnnotationNames.METADATA_DESC) return null + + return object : AnnotationVisitor(Opcodes.API_VERSION) { + override fun visit(name: String, value: Any) { + if (name != JvmAnnotationNames.METADATA_EXTRA_INT_FIELD_NAME) return + + isPreRelease = (value as Int and JvmAnnotationNames.METADATA_PRE_RELEASE_FLAG) != 0 + } + } + } + }, 0) + + TestCase.assertEquals("Pre-release flag of the class file has incorrect value", shouldBePreRelease, isPreRelease) + } + + private fun checkPreReleaseness(file: File, shouldBePreRelease: Boolean) { + checkPreReleaseness(file.readBytes(), shouldBePreRelease) + } } diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 0d9b22c8f59..145614945dc 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -596,7 +596,7 @@ class LanguageVersionSettingsImpl @JvmOverloads constructor( } } - override fun isPreRelease(): Boolean = !languageVersion.isStable || + override fun isPreRelease(): Boolean = languageVersion.isPreRelease() || specificFeatures.any { (feature, state) -> state == LanguageFeature.State.ENABLED && feature.forcesPreReleaseBinariesIfEnabled() } @@ -607,6 +607,12 @@ class LanguageVersionSettingsImpl @JvmOverloads constructor( } } +fun LanguageVersion.isPreRelease(): Boolean { + if (!isStable) return true + + return KotlinCompilerVersion.isPreRelease() && this == LanguageVersion.LATEST_STABLE +} + fun LanguageFeature.forcesPreReleaseBinariesIfEnabled(): Boolean { val isFeatureNotReleasedYet = sinceVersion?.isStable != true return isFeatureNotReleasedYet && kind.forcesPreReleaseBinaries