Restore KotlinCompilerVersion.IS_PRE_RELEASE

This commit is a slightly modified revert of 4f29c113.
IS_PRE_RELEASE allows to make LATEST_STABLE version behave as
experimental when this flag is set to true.
The general goal is to prepare fix of KT-62058; after this commit
one can do it by changing IS_PRE_RELEASE flag to true.
The fix of KT-62058 is planned to be done during bootstrapping.
This preparation and the future fix are parts of umbrella KT-61951.
This commit is contained in:
Mikhail Glukhikh
2023-09-20 15:45:08 +02:00
committed by Space Team
parent 850201a65c
commit 156097fe17
13 changed files with 152 additions and 12 deletions
@@ -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
@@ -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"
);
}
}
}
@@ -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<PackagePartsCacheData> =
packagePartProvider.findPackageParts(packageFqName.asString()).mapNotNull { partName ->
@@ -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)
@@ -0,0 +1,5 @@
package a
class A
fun A.foo() = ""
@@ -0,0 +1,7 @@
package usage
import a.*
fun baz(param: A) {
param.foo()
}
@@ -0,0 +1,8 @@
package usage
import a.*
fun baz(param: A): A {
foo()
return param
}
@@ -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 <T> getFlag(flag: AnalysisFlag<T>): T = analysisFlags[flag] as T? ?: flag.defaultValue
@@ -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 <T> 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)
}
}
@@ -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