diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt index 9d833e4e2f3..3101de8f2f4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.codegen.createFreeFakeLocalPropertyDescriptor import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.* import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.KotlinTypeMapperBase +import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.load.java.JvmAbi @@ -30,6 +31,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPrivateApi import org.jetbrains.kotlin.resolve.descriptorUtil.nonSourceAnnotations +import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmDefaultAnnotation import org.jetbrains.kotlin.resolve.jvm.annotations.isCompiledToJvmDefaultIfNoAbstract import org.jetbrains.kotlin.serialization.DescriptorSerializer import org.jetbrains.kotlin.serialization.DescriptorSerializer.Companion.writeVersionRequirement @@ -102,15 +104,19 @@ class JvmSerializerExtension @JvmOverloads constructor( builder: ProtoBuf.Class.Builder, versionRequirementTable: MutableVersionRequirementTable ) { - if ( - isInterface(classDescriptor) && - classDescriptor.unsubstitutedMemberScope.getContributedDescriptors().any { - it is CallableMemberDescriptor && it.isCompiledToJvmDefaultIfNoAbstract(jvmDefaultMode) //TODO: new requirements for new option + if (isInterface(classDescriptor)) { + if (jvmDefaultMode == JvmDefaultMode.ENABLE && classDescriptor.unsubstitutedMemberScope.getContributedDescriptors().any { + it is CallableMemberDescriptor && it.hasJvmDefaultAnnotation() + }) { + builder.addVersionRequirement( + writeVersionRequirement(1, 2, 40, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable) + ) + } + if (jvmDefaultMode == JvmDefaultMode.ALL_INCOMPATIBLE) { + builder.addVersionRequirement( + writeVersionRequirement(1, 4, 0, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable) + ) } - ) { - builder.addVersionRequirement( - writeVersionRequirement(1, 2, 40, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable) - ) } } diff --git a/compiler/testData/versionRequirement/allCompatibilityJvmDefault.kt b/compiler/testData/versionRequirement/allCompatibilityJvmDefault.kt new file mode 100644 index 00000000000..84f2bd81e0f --- /dev/null +++ b/compiler/testData/versionRequirement/allCompatibilityJvmDefault.kt @@ -0,0 +1,27 @@ +package test + +interface Base { + fun foo() {} +} + +interface Derived : Base + +interface BaseWithProperty { + val prop: String + get() = "123" +} + +interface DerivedWithProperty : BaseWithProperty + +interface Empty + +interface EmptyWithNested { + interface Nested +} + +interface WithAbstractDeclaration { + fun foo() + var prop: String +} + +interface DerivedFromWithAbstractDeclaration : WithAbstractDeclaration \ No newline at end of file diff --git a/compiler/testData/versionRequirement/allJvmDefault.kt b/compiler/testData/versionRequirement/allJvmDefault.kt new file mode 100644 index 00000000000..84f2bd81e0f --- /dev/null +++ b/compiler/testData/versionRequirement/allJvmDefault.kt @@ -0,0 +1,27 @@ +package test + +interface Base { + fun foo() {} +} + +interface Derived : Base + +interface BaseWithProperty { + val prop: String + get() = "123" +} + +interface DerivedWithProperty : BaseWithProperty + +interface Empty + +interface EmptyWithNested { + interface Nested +} + +interface WithAbstractDeclaration { + fun foo() + var prop: String +} + +interface DerivedFromWithAbstractDeclaration : WithAbstractDeclaration \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/AbstractVersionRequirementTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/AbstractVersionRequirementTest.kt index 3b09478cee6..1bdac9ccf45 100644 --- a/compiler/tests/org/jetbrains/kotlin/serialization/AbstractVersionRequirementTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/serialization/AbstractVersionRequirementTest.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.serialization +import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.descriptors.ClassDescriptor @@ -28,19 +29,17 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { expectedVersionKind: ProtoBuf.VersionRequirement.VersionKind, expectedErrorCode: Int?, customLanguageVersion: LanguageVersion = LanguageVersionSettingsImpl.DEFAULT.languageVersion, - fqNames: List + analysisFlags: Map, Any?> = emptyMap(), + fqNamesWithRequirements: List, + fqNamesWithoutRequirement: List = emptyList() ) { - compileFiles(listOf(File("compiler/testData/versionRequirement/${getTestName(true)}.kt")), tmpdir, customLanguageVersion) + compileFiles(listOf(File("compiler/testData/versionRequirement/${getTestName(true)}.kt")), tmpdir, customLanguageVersion, analysisFlags) val module = loadModule(tmpdir) - for (fqName in fqNames) { + for (fqName in fqNamesWithRequirements) { val descriptor = module.findUnambiguousDescriptorByFqName(fqName) - val requirement = when (descriptor) { - is DeserializedMemberDescriptor -> descriptor.versionRequirements.singleOrNull() - is DeserializedClassDescriptor -> descriptor.versionRequirements.singleOrNull() - else -> throw AssertionError("Unknown descriptor: $descriptor") - } ?: throw AssertionError("No VersionRequirement for $descriptor") + val requirement = extractRequirement(descriptor) ?: throw AssertionError("No VersionRequirement for $descriptor") assertEquals("Incorrect version for $fqName", expectedVersionRequirement, requirement.version) assertEquals("Incorrect level for $fqName", expectedLevel, requirement.level) @@ -48,6 +47,21 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { assertEquals("Incorrect versionKind for $fqName", expectedVersionKind, requirement.kind) assertEquals("Incorrect errorCode for $fqName", expectedErrorCode, requirement.errorCode) } + + for (fqName in fqNamesWithoutRequirement) { + val descriptor = module.findUnambiguousDescriptorByFqName(fqName) + + val requirenment = extractRequirement(descriptor) + assertNull("Expecting absence of any requirements for $fqName, but `$requirenment`", requirenment) + } + } + + private fun extractRequirement(descriptor: DeclarationDescriptor): VersionRequirement? { + return when (descriptor) { + is DeserializedMemberDescriptor -> descriptor.versionRequirements.singleOrNull() + is DeserializedClassDescriptor -> descriptor.versionRequirements.singleOrNull() + else -> throw AssertionError("Unknown descriptor: $descriptor") + } } private fun ModuleDescriptor.findUnambiguousDescriptorByFqName(fqName: String): DeclarationDescriptor { @@ -71,7 +85,12 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { return descriptor } - protected abstract fun compileFiles(files: List, outputDirectory: File, languageVersion: LanguageVersion) + protected abstract fun compileFiles( + files: List, + outputDirectory: File, + languageVersion: LanguageVersion, + analysisFlags: Map, Any?> + ) protected abstract fun loadModule(directory: File): ModuleDescriptor @@ -79,7 +98,7 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { doTest( VersionRequirement.Version(1, 3), DeprecationLevel.ERROR, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, customLanguageVersion = LanguageVersion.KOTLIN_1_3, - fqNames = listOf( + fqNamesWithRequirements = listOf( "test.topLevel", "test.Foo.member", "test.Foo.", @@ -96,7 +115,7 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { doTest( VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, 42, - fqNames = listOf( + fqNamesWithRequirements = listOf( "test.Klass", "test.Konstructor.", "test.Typealias", @@ -109,7 +128,7 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { fun testApiVersionViaAnnotation() { doTest( VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", ProtoBuf.VersionRequirement.VersionKind.API_VERSION, 42, - fqNames = listOf( + fqNamesWithRequirements = listOf( "test.Klass", "test.Konstructor.", "test.Typealias", @@ -123,7 +142,7 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { doTest( VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, 42, - fqNames = listOf( + fqNamesWithRequirements = listOf( "test.Klass", "test.Konstructor.", "test.Typealias", @@ -137,7 +156,7 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { doTest( VersionRequirement.Version(1, 1, 50), DeprecationLevel.HIDDEN, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, - fqNames = listOf("test.Klass") + fqNamesWithRequirements = listOf("test.Klass") ) } @@ -145,7 +164,7 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { doTest( VersionRequirement.Version(1, 3), DeprecationLevel.ERROR, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, customLanguageVersion = LanguageVersion.KOTLIN_1_3, - fqNames = listOf( + fqNamesWithRequirements = listOf( "test.Outer.Inner.Deep", "test.Outer.Inner.Deep.", "test.Outer.Inner.Deep.f", @@ -160,7 +179,7 @@ abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { fun testInlineClassesAndRelevantDeclarations() { doTest( VersionRequirement.Version(1, 3), DeprecationLevel.ERROR, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, - fqNames = listOf( + fqNamesWithRequirements = listOf( "test.IC", "test.Ctor.", "test.simpleFun", diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/JvmVersionRequirementTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/JvmVersionRequirementTest.kt index 7b4fe7374dd..c421ab38907 100644 --- a/compiler/tests/org/jetbrains/kotlin/serialization/JvmVersionRequirementTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/serialization/JvmVersionRequirementTest.kt @@ -31,7 +31,12 @@ import org.jetbrains.kotlin.test.TestJdkKind import java.io.File class JvmVersionRequirementTest : AbstractVersionRequirementTest() { - override fun compileFiles(files: List, outputDirectory: File, languageVersion: LanguageVersion) { + override fun compileFiles( + files: List, + outputDirectory: File, + languageVersion: LanguageVersion, + analysisFlags: Map, Any?> + ) { LoadDescriptorUtil.compileKotlinToDirAndGetModule( listOf(File("compiler/testData/versionRequirement/${getTestName(true)}.kt")), outputDirectory, KotlinCoreEnvironment.createForTests( @@ -41,10 +46,7 @@ class JvmVersionRequirementTest : AbstractVersionRequirementTest() { languageVersionSettings = LanguageVersionSettingsImpl( languageVersion, ApiVersion.createByLanguageVersion(languageVersion), - mapOf( - JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ENABLE, - AnalysisFlags.explicitApiVersion to true - ), + analysisFlags.toMap() + mapOf(AnalysisFlags.explicitApiVersion to true), emptyMap() ) }, @@ -64,24 +66,60 @@ class JvmVersionRequirementTest : AbstractVersionRequirementTest() { fun testJvmDefault() { doTest( VersionRequirement.Version(1, 2, 40), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, - fqNames = listOf( + analysisFlags = mapOf(JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ENABLE), + fqNamesWithRequirements = listOf( "test.Base", "test.Derived" ) ) } + fun testAllJvmDefault() { + doTest( + VersionRequirement.Version(1, 4, 0), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, + analysisFlags = mapOf(JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ALL_INCOMPATIBLE), + fqNamesWithRequirements = listOf( + "test.Base", + "test.Derived", + "test.BaseWithProperty", + "test.DerivedWithProperty", + "test.Empty", + "test.EmptyWithNested", + "test.WithAbstractDeclaration", + "test.DerivedFromWithAbstractDeclaration" + ) + ) + } + + fun testAllCompatibilityJvmDefault() { + doTest( + VersionRequirement.Version(1, 4, 0), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, + analysisFlags = mapOf(JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ALL_COMPATIBILITY), + fqNamesWithRequirements = emptyList(), + fqNamesWithoutRequirement = listOf( + "test.Base", + "test.Derived", + "test.BaseWithProperty", + "test.DerivedWithProperty", + "test.Empty", + "test.EmptyWithNested", + "test.WithAbstractDeclaration", + "test.DerivedFromWithAbstractDeclaration" + ) + ) + } + fun testJvmFieldInInterfaceCompanion() { doTest( VersionRequirement.Version(1, 2, 70), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, - fqNames = listOf("test.Base.Companion.foo") + fqNamesWithRequirements = listOf("test.Base.Companion.foo") ) } fun testInlineParameterNullCheck() { doTest( VersionRequirement.Version(1, 3, 50), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, - fqNames = listOf( + fqNamesWithRequirements = listOf( "test.doRun", "test.lambdaVarProperty", "test.extensionProperty" @@ -94,7 +132,7 @@ class JvmVersionRequirementTest : AbstractVersionRequirementTest() { doTest( VersionRequirement.Version(1, 1), DeprecationLevel.ERROR, null, LANGUAGE_VERSION, null, customLanguageVersion = LanguageVersion.KOTLIN_1_2, - fqNames = listOf( + fqNamesWithRequirements = listOf( "test.topLevel", "test.Foo.member", "test.Foo.", diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/js/JsVersionRequirementTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/js/JsVersionRequirementTest.kt index 460cafd0ffd..28255e020b4 100644 --- a/compiler/tests/org/jetbrains/kotlin/serialization/js/JsVersionRequirementTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/serialization/js/JsVersionRequirementTest.kt @@ -8,10 +8,7 @@ package org.jetbrains.kotlin.serialization.js import org.jetbrains.kotlin.cli.common.output.writeAllTo import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.config.ApiVersion -import org.jetbrains.kotlin.config.LanguageVersion -import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl -import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.context.ContextForNewModule import org.jetbrains.kotlin.context.MutableModuleContext import org.jetbrains.kotlin.context.ProjectContext @@ -33,7 +30,12 @@ import org.jetbrains.kotlin.test.TestJdkKind import java.io.File class JsVersionRequirementTest : AbstractVersionRequirementTest() { - override fun compileFiles(files: List, outputDirectory: File, languageVersion: LanguageVersion) { + override fun compileFiles( + files: List, + outputDirectory: File, + languageVersion: LanguageVersion, + analysisFlags: Map, Any?> + ) { val environment = createEnvironment(languageVersion) val ktFiles = files.map { file -> KotlinTestUtils.createFile(file.name, file.readText(), environment.project) } val trace = BindingTraceContext()