diff --git a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt index 6f709538272..d336e345987 100644 --- a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt +++ b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.protobuf.MessageLite import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry +import org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface import org.jetbrains.kotlin.resolve.MemberComparator import org.jetbrains.kotlin.resolve.RequireKotlinNames import org.jetbrains.kotlin.resolve.annotations.hasJvmDefaultAnnotation @@ -142,6 +143,8 @@ class DescriptorSerializer private constructor( val requirement = serializeVersionRequirement(classDescriptor) if (requirement != null) { builder.versionRequirement = requirement + } else { + writeVersionRequirementForJvmDefaultIfNeeded(classDescriptor, builder) } val versionRequirementTableProto = versionRequirementTable.serialize() @@ -239,8 +242,6 @@ class DescriptorSerializer private constructor( } else if (descriptor.isSuspendOrHasSuspendTypesInSignature()) { builder.versionRequirement = writeVersionRequirement(LanguageFeature.Coroutines) - } else if (descriptor.hasJvmDefaultAnnotation()) { - builder.versionRequirement = writeVersionRequirement(1, 2, 40) } extension.serializeProperty(descriptor, builder) @@ -311,8 +312,6 @@ class DescriptorSerializer private constructor( builder.versionRequirement = requirement } else if (descriptor.isSuspendOrHasSuspendTypesInSignature()) { builder.versionRequirement = writeVersionRequirement(LanguageFeature.Coroutines) - } else if (descriptor.hasJvmDefaultAnnotation()) { - builder.versionRequirement = writeVersionRequirement(1, 2, 40) } contractSerializer.serializeContractOfFunctionIfAny(descriptor, builder, this) @@ -619,23 +618,36 @@ class DescriptorSerializer private constructor( return builder } - private fun writeVersionRequirement(languageFeature: LanguageFeature): Int { - val languageVersion = languageFeature.sinceVersion!! - val requirement = ProtoBuf.VersionRequirement.newBuilder().apply { - VersionRequirement.Version(languageVersion.major, languageVersion.minor).encode( - writeVersion = { version = it }, - writeVersionFull = { versionFull = it } - ) + // Interfaces which have @JvmDefault members somewhere in the hierarchy need the compiler 1.2.40+ + // so that the generated bridges in subclasses would call the super members correctly + private fun writeVersionRequirementForJvmDefaultIfNeeded(classDescriptor: ClassDescriptor, builder: ProtoBuf.Class.Builder) { + if ( + isInterface(classDescriptor) && + classDescriptor.unsubstitutedMemberScope.getContributedDescriptors().any { + it is CallableMemberDescriptor && it.hasJvmDefaultAnnotation() + } + ) { + builder.versionRequirement = writeVersionRequirement(1, 2, 40, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION) } - return versionRequirementTable[requirement] } - private fun writeVersionRequirement(major: Int, minor: Int, patch: Int): Int { + private fun writeVersionRequirement(languageFeature: LanguageFeature): Int { + val languageVersion = languageFeature.sinceVersion!! + return writeVersionRequirement( + languageVersion.major, languageVersion.minor, 0, + ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION + ) + } + + private fun writeVersionRequirement(major: Int, minor: Int, patch: Int, versionKind: ProtoBuf.VersionRequirement.VersionKind): Int { val requirement = ProtoBuf.VersionRequirement.newBuilder().apply { VersionRequirement.Version(major, minor, patch).encode( writeVersion = { version = it }, writeVersionFull = { versionFull = it } ) + if (versionKind != defaultInstanceForType.versionKind) { + this.versionKind = versionKind + } } return versionRequirementTable[requirement] } diff --git a/compiler/testData/versionRequirement/jvmDefault.kt b/compiler/testData/versionRequirement/jvmDefault.kt new file mode 100644 index 00000000000..81ea75f0429 --- /dev/null +++ b/compiler/testData/versionRequirement/jvmDefault.kt @@ -0,0 +1,8 @@ +package test + +interface Base { + @JvmDefault + fun foo() {} +} + +interface Derived : Base diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/VersionRequirementTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/VersionRequirementTest.kt index d9b5fb04fc1..08ec492ad7c 100644 --- a/compiler/tests/org/jetbrains/kotlin/serialization/VersionRequirementTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/serialization/VersionRequirementTest.kt @@ -18,12 +18,14 @@ package org.jetbrains.kotlin.serialization import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PackageViewDescriptor import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.ProtoBuf.VersionRequirement.VersionKind.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered @@ -46,8 +48,20 @@ class VersionRequirementTest : TestCaseWithTmpdir() { vararg fqNames: String ) { LoadDescriptorUtil.compileKotlinToDirAndGetModule( - listOf(File("compiler/testData/versionRequirement/${getTestName(true)}.kt")), tmpdir, - KotlinTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(testRootDisposable) + listOf(File("compiler/testData/versionRequirement/${getTestName(true)}.kt")), tmpdir, + KotlinCoreEnvironment.createForTests( + testRootDisposable, + KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, tmpdir).apply { + put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8) + languageVersionSettings = LanguageVersionSettingsImpl( + LanguageVersionSettingsImpl.DEFAULT.languageVersion, + LanguageVersionSettingsImpl.DEFAULT.apiVersion, + mapOf(AnalysisFlag.enableJvmDefault to true), + emptyMap() + ) + }, + EnvironmentConfigFiles.JVM_CONFIG_FILES + ) ) val (_, module) = JvmResolveUtil.analyze( @@ -99,7 +113,7 @@ class VersionRequirementTest : TestCaseWithTmpdir() { } fun testSuspendFun() { - doTest(VersionRequirement.Version(1, 1), DeprecationLevel.ERROR, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, + doTest(VersionRequirement.Version(1, 1), DeprecationLevel.ERROR, null, LANGUAGE_VERSION, null, "test.topLevel", "test.Foo.member", "test.Foo.", @@ -112,7 +126,7 @@ class VersionRequirementTest : TestCaseWithTmpdir() { } fun testLanguageVersionViaAnnotation() { - doTest(VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, 42, + doTest(VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", LANGUAGE_VERSION, 42, "test.Klass", "test.Konstructor.", "test.Typealias", @@ -122,7 +136,7 @@ class VersionRequirementTest : TestCaseWithTmpdir() { } fun testApiVersionViaAnnotation() { - doTest(VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", ProtoBuf.VersionRequirement.VersionKind.API_VERSION, 42, + doTest(VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", API_VERSION, 42, "test.Klass", "test.Konstructor.", "test.Typealias", @@ -132,7 +146,7 @@ class VersionRequirementTest : TestCaseWithTmpdir() { } fun testCompilerVersionViaAnnotation() { - doTest(VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, 42, + doTest(VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", COMPILER_VERSION, 42, "test.Klass", "test.Konstructor.", "test.Typealias", @@ -142,8 +156,16 @@ class VersionRequirementTest : TestCaseWithTmpdir() { } fun testPatchVersion() { - doTest(VersionRequirement.Version(1, 1, 50), DeprecationLevel.HIDDEN, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, + doTest(VersionRequirement.Version(1, 1, 50), DeprecationLevel.HIDDEN, null, LANGUAGE_VERSION, null, "test.Klass" ) } + + fun testJvmDefault() { + doTest( + VersionRequirement.Version(1, 2, 40), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, + "test.Base", + "test.Derived" + ) + } }