From 71c134e54fc6af5449591d230239a57c916ef556 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Mon, 8 Mar 2021 16:18:11 +0100 Subject: [PATCH] Fix checker for -Xjvm-defaults --- .../resolve/jvm/checkers/JvmDefaultChecker.kt | 18 +++++++++++------- .../library/a.kt | 5 +++++ .../output.txt | 4 ++++ .../source.kt | 17 +++++++++++++++++ .../CompileKotlinAgainstCustomBinariesTest.kt | 5 +++++ 5 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/library/a.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/source.kt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt index 97fcbcd3a00..47e047331bd 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt @@ -28,33 +28,37 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.util.getNonPrivateTraitMembersForDelegation import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult -class JvmDefaultChecker(val jvmTarget: JvmTarget, project: Project) : DeclarationChecker { +class JvmDefaultChecker(private val jvmTarget: JvmTarget, private val project: Project) : DeclarationChecker { private val ideService = LanguageVersionSettingsProvider.getInstance(project) override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { val jvmDefaultMode = context.languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultMode) - descriptor.annotations.findAnnotation(JVM_DEFAULT_FQ_NAME)?.let { annotationDescriptor -> + val jvmDefaultAnnotation = descriptor.annotations.findAnnotation(JVM_DEFAULT_FQ_NAME) + jvmDefaultAnnotation?.let { annotationDescriptor -> val reportOn = DescriptorToSourceUtils.getSourceFromAnnotation(annotationDescriptor) ?: declaration if (!isInterface(descriptor.containingDeclaration)) { context.trace.report(ErrorsJvm.JVM_DEFAULT_NOT_IN_INTERFACE.on(reportOn)) + return@check } else if (jvmTarget == JvmTarget.JVM_1_6) { context.trace.report(ErrorsJvm.JVM_DEFAULT_IN_JVM6_TARGET.on(reportOn, "JvmDefault")) + return@check } else if (!jvmDefaultMode.isEnabled) { context.trace.report(ErrorsJvm.JVM_DEFAULT_IN_DECLARATION.on(declaration, "JvmDefault")) + return@check } - return@check } descriptor.annotations.findAnnotation(JVM_DEFAULT_NO_COMPATIBILITY_FQ_NAME)?.let { annotationDescriptor -> val reportOn = DescriptorToSourceUtils.getSourceFromAnnotation(annotationDescriptor) ?: declaration if (jvmTarget == JvmTarget.JVM_1_6) { context.trace.report(ErrorsJvm.JVM_DEFAULT_IN_JVM6_TARGET.on(reportOn, "JvmDefaultWithoutCompatibility")) + return@check } else if (!jvmDefaultMode.isEnabled) { context.trace.report(ErrorsJvm.JVM_DEFAULT_IN_DECLARATION.on(reportOn, "JvmDefaultWithoutCompatibility")) + return@check } - return@check } if (descriptor is ClassDescriptor) { @@ -68,7 +72,7 @@ class JvmDefaultChecker(val jvmTarget: JvmTarget, project: Project) : Declaratio } - if (!jvmDefaultMode.forAllMethodsWithBody && isInterface(descriptor.containingDeclaration)) { + if (jvmDefaultAnnotation == null && !jvmDefaultMode.forAllMethodsWithBody && isInterface(descriptor.containingDeclaration)) { val memberDescriptor = descriptor as? CallableMemberDescriptor ?: return if (descriptor is PropertyAccessorDescriptor) return @@ -87,7 +91,7 @@ class JvmDefaultChecker(val jvmTarget: JvmTarget, project: Project) : Declaratio if (!jvmDefaultMode.isEnabled || descriptor !is ClassDescriptor || isInterface(descriptor) || isAnnotationClass(descriptor)) return // JvmDefaults members checks across class hierarchy: - // 1. If in old scheme class have implicit override with different signature than overriden method (e.g. generic specialization) + // 1. If in old scheme class have implicit override with different signature than overridden method (e.g. generic specialization) // report error because absent of it's can affect library ABI // 2. If it's mixed hierarchy with implicit override in base class and override one in inherited derived interface report error. // Otherwise the implicit class override would be used for dispatching method calls (but not more specialized) @@ -225,7 +229,7 @@ class JvmDefaultChecker(val jvmTarget: JvmTarget, project: Project) : Declaratio private fun CallableMemberDescriptor.isCompiledToJvmDefaultWithProperMode(compilationDefaultMode: JvmDefaultMode): Boolean { val jvmDefault = - if (this is DeserializedDescriptor) compilationDefaultMode else ideService?.getModuleLanguageVersionSettings(module) + if (this is DeserializedDescriptor) compilationDefaultMode/*doesn't matter*/ else ideService?.getModuleLanguageVersionSettings(module) ?.getFlag(JvmAnalysisFlags.jvmDefaultMode) ?: compilationDefaultMode return isCompiledToJvmDefault(jvmDefault) } diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/library/a.kt new file mode 100644 index 00000000000..1f88c373c8b --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/library/a.kt @@ -0,0 +1,5 @@ +package base + +interface UExpression { + fun evaluate(): Any? = "fail" +} \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/output.txt new file mode 100644 index 00000000000..2760832f5d6 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/output.txt @@ -0,0 +1,4 @@ +compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/source.kt:12:7: error: explicit override is required for 'public open fun evaluate(): Any? defined in KotlinUBinaryExpressionWithType' in the '-Xjvm-default=all-compatibility' mode. Otherwise, implicit class override 'public open fun evaluate(): Any? defined in KotlinAbstractUExpression' (compiled in the old -Xjvm-default mode) is not fully overridden and might be incorrectly called at runtime +class KotlinUBinaryExpressionWithType : KotlinAbstractUExpression(), KotlinEvaluatableUElement {} + ^ +COMPILATION_ERROR diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/source.kt new file mode 100644 index 00000000000..9f467c0cc51 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultClashWithNoCompatibility/source.kt @@ -0,0 +1,17 @@ +import base.* + +interface KotlinEvaluatableUElement : UExpression { + override fun evaluate(): Any? { + return "OK" + } +} + +abstract class KotlinAbstractUExpression() : UExpression {} + +@JvmDefaultWithoutCompatibility +class KotlinUBinaryExpressionWithType : KotlinAbstractUExpression(), KotlinEvaluatableUElement {} + +fun box(): String { + val foo = KotlinUBinaryExpressionWithType() + return foo.evaluate() as String +} diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt index e8230b478c4..20cd32ce46f 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt @@ -593,6 +593,11 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-jvm-target", "1.8", "-Xjvm-default=all")) } + fun testJvmDefaultClashWithNoCompatibility() { + val library = compileLibrary("library", additionalOptions = listOf("-Xjvm-default=disable")) + compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-jvm-target", "1.8", "-Xjvm-default=all-compatibility")) + } + fun testJvmDefaultCompatibilityAgainstJava() { val library = compileLibrary("library", additionalOptions = listOf("-Xjvm-default=disable")) compileKotlin(