Skip to content

Commit ddd02fe

Browse files
Mikhael BogdanovSpace
Mikhael Bogdanov
authored and
Space
committed
JvmDefault. Allow non default inheritance with special flag
#KT-47000 (cherry picked from commit afc149d)
1 parent 206d7d9 commit ddd02fe

File tree

24 files changed

+464
-6
lines changed

24 files changed

+464
-6
lines changed

compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt

+4
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
350350
)
351351
var jvmDefault: String by FreezableVar(JvmDefaultMode.DEFAULT.description)
352352

353+
@Argument(value = "-Xjvm-default-allow-non-default-inheritance", description = "Allow inheritance from 'all*' modes for 'disable' one")
354+
var jvmDefaultAllowDisableAgainstAll: Boolean by FreezableVar(false)
355+
353356
@Argument(
354357
value = "-Xdefault-script-extension",
355358
valueDescription = "<script filename extension>",
@@ -549,6 +552,7 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
549552
result[AnalysisFlags.allowUnstableDependencies] = allowUnstableDependencies || useFir
550553
result[JvmAnalysisFlags.disableUltraLightClasses] = disableUltraLightClasses
551554
result[JvmAnalysisFlags.useIR] = !useOldBackend
555+
result[JvmAnalysisFlags.jvmDefaultAllowNonDefaultInheritance] = jvmDefaultAllowDisableAgainstAll
552556
return result
553557
}
554558

compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmAnalysisFlags.kt

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ object JvmAnalysisFlags {
1818
@JvmStatic
1919
val jvmDefaultMode by Delegates.JvmDefaultModeDisabledByDefault
2020

21+
@JvmStatic
22+
val jvmDefaultAllowNonDefaultInheritance by AnalysisFlag.Delegates.Boolean
23+
2124
@JvmStatic
2225
val inheritMultifileParts by AnalysisFlag.Delegates.Boolean
2326

compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java

+34
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmDefaultChecker.kt

+9-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class JvmDefaultChecker(private val jvmTarget: JvmTarget, private val project: P
3333

3434
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
3535
val jvmDefaultMode = context.languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultMode)
36+
val allowNonDefaultInheritance = context.languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultAllowNonDefaultInheritance)
3637

3738
val jvmDefaultAnnotation = descriptor.annotations.findAnnotation(JVM_DEFAULT_FQ_NAME)
3839
jvmDefaultAnnotation?.let { annotationDescriptor ->
@@ -60,13 +61,15 @@ class JvmDefaultChecker(private val jvmTarget: JvmTarget, private val project: P
6061
}
6162
}
6263

63-
if (descriptor is ClassDescriptor) {
64-
val hasDeclaredJvmDefaults =
65-
descriptor.unsubstitutedMemberScope.getContributedDescriptors().filterIsInstance<CallableMemberDescriptor>().any {
66-
it.kind.isReal && it.isCompiledToJvmDefault(jvmDefaultMode)
64+
if (!allowNonDefaultInheritance) {
65+
if (descriptor is ClassDescriptor) {
66+
val hasDeclaredJvmDefaults =
67+
descriptor.unsubstitutedMemberScope.getContributedDescriptors().filterIsInstance<CallableMemberDescriptor>().any {
68+
it.kind.isReal && it.isCompiledToJvmDefault(jvmDefaultMode)
69+
}
70+
if (!hasDeclaredJvmDefaults && !checkJvmDefaultsInHierarchy(descriptor, jvmDefaultMode)) {
71+
context.trace.report(ErrorsJvm.JVM_DEFAULT_THROUGH_INHERITANCE.on(declaration))
6772
}
68-
if (!hasDeclaredJvmDefaults && !checkJvmDefaultsInHierarchy(descriptor, jvmDefaultMode)) {
69-
context.trace.report(ErrorsJvm.JVM_DEFAULT_THROUGH_INHERITANCE.on(declaration))
7073
}
7174
}
7275

compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/builders/LanguageVersionSettingsBuilder.kt

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class LanguageVersionSettingsBuilder {
7171
analysisFlag(AnalysisFlags.allowKotlinPackage, trueOrNull(LanguageSettingsDirectives.ALLOW_KOTLIN_PACKAGE in directives)),
7272

7373
analysisFlag(JvmAnalysisFlags.jvmDefaultMode, directives.singleOrZeroValue(LanguageSettingsDirectives.JVM_DEFAULT_MODE)),
74+
analysisFlag(JvmAnalysisFlags.jvmDefaultAllowNonDefaultInheritance, trueOrNull(LanguageSettingsDirectives.JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE in directives)),
7475
analysisFlag(JvmAnalysisFlags.inheritMultifileParts, trueOrNull(LanguageSettingsDirectives.INHERIT_MULTIFILE_PARTS in directives)),
7576
analysisFlag(JvmAnalysisFlags.sanitizeParentheses, trueOrNull(LanguageSettingsDirectives.SANITIZE_PARENTHESES in directives)),
7677
analysisFlag(JvmAnalysisFlags.enableJvmPreview, trueOrNull(LanguageSettingsDirectives.ENABLE_JVM_PREVIEW in directives)),

compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/directives/LanguageSettingsDirectives.kt

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ object LanguageSettingsDirectives : SimpleDirectivesContainer() {
6363
additionalParser = JvmDefaultMode.Companion::fromStringOrNull
6464
)
6565

66+
val JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE by directive(
67+
description = "Configures corresponding analysis flag (JvmAnalysisFlags.jvmDefaultAllowNonDefaultInheritance)",
68+
)
69+
6670
val INHERIT_MULTIFILE_PARTS by directive(
6771
description = "Enables corresponding analysis flag (JvmAnalysisFlags.inheritMultifileParts)"
6872
)

compiler/testData/cli/jvm/extraHelp.out

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ where advanced options include:
7575
(annotating an existing method can break binary compatibility)
7676
-Xjvm-default=compatibility Allow usages of @JvmDefault; generate a compatibility accessor
7777
in the 'DefaultImpls' class in addition to the default interface method
78+
-Xjvm-default-allow-non-default-inheritance
79+
Allow inheritance from 'all*' modes for 'disable' one
7880
-Xklib=<path> Paths to cross-platform libraries in .klib format
7981
-Xlambdas={class|indy} Select code generation scheme for lambdas.
8082
-Xlambdas=indy Generate lambdas using `invokedynamic` with `LambdaMetafactory.metafactory`. Requires `-jvm-target 1.8` or greater.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// TARGET_BACKEND: JVM
2+
// IGNORE_BACKEND_FIR: JVM_IR
3+
// FULL_JDK
4+
// JVM_TARGET: 1.8
5+
// WITH_RUNTIME
6+
// MODULE: lib
7+
// !JVM_DEFAULT_MODE: all
8+
// FILE: Foo.kt
9+
10+
interface Foo {
11+
fun toOverride(): String = "fail"
12+
13+
fun nonOverride(): String = "K"
14+
}
15+
16+
// MODULE: main(lib)
17+
// !JVM_DEFAULT_MODE: disable
18+
// !JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE
19+
// FILE: main.kt
20+
21+
interface Derived : Foo {
22+
override fun toOverride(): String {
23+
return "O"
24+
}
25+
}
26+
27+
class DerivedClass : Derived
28+
29+
30+
fun box(): String {
31+
checkMethodExists(DerivedClass::class.java, "toOverride")
32+
checkNoMethod(DerivedClass::class.java, "nonOverride")
33+
34+
val value = DerivedClass()
35+
return value.toOverride() + value.nonOverride()
36+
}
37+
38+
fun checkNoMethod(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) {
39+
try {
40+
clazz.getDeclaredMethod(name, *parameterTypes)
41+
}
42+
catch (e: NoSuchMethodException) {
43+
return
44+
}
45+
throw AssertionError("fail: method $name was found in " + clazz)
46+
}
47+
48+
fun checkMethodExists(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) {
49+
try {
50+
clazz.getDeclaredMethod(name, *parameterTypes)
51+
return
52+
}
53+
catch (e: NoSuchMethodException) {
54+
throw AssertionError("fail: method $name was not found in " + clazz, e)
55+
}
56+
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// TARGET_BACKEND: JVM
2+
// IGNORE_BACKEND_FIR: JVM_IR
3+
// FULL_JDK
4+
// JVM_TARGET: 1.8
5+
// WITH_RUNTIME
6+
// MODULE: lib
7+
// !JVM_DEFAULT_MODE: all
8+
// FILE: Foo.kt
9+
10+
interface Foo {
11+
fun toOverride(): List<String> = null!!
12+
13+
fun nonOverride(): List<String> = Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName }
14+
}
15+
16+
// MODULE: main(lib)
17+
// !JVM_DEFAULT_MODE: disable
18+
// !JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE
19+
// FILE: main.kt
20+
21+
interface Derived : Foo {
22+
override fun toOverride() = Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName }
23+
}
24+
25+
class DerivedClass : Derived
26+
27+
28+
fun box(): String {
29+
val override = DerivedClass().toOverride()
30+
if (override[1] != "Derived\$DefaultImpls.toOverride") return "fail 1: ${override[1]}"
31+
if (override[2] != "DerivedClass.toOverride") return "fail 2: ${override[2]}"
32+
if (override[3] != "MainKt.box") return "fail 3: ${override[3]}"
33+
34+
val nonOverride = DerivedClass().nonOverride()
35+
if (nonOverride[1] != "Foo.nonOverride") return "fail 3: ${nonOverride[1]}"
36+
if (nonOverride[2] != "MainKt.box") return "fail 4: ${nonOverride[2]}"
37+
38+
return "OK"
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// CHECK_BYTECODE_LISTING
2+
// TARGET_BACKEND: JVM
3+
// IGNORE_BACKEND_FIR: JVM_IR
4+
// JVM_TARGET: 1.8
5+
// WITH_RUNTIME
6+
// MODULE: lib
7+
// !JVM_DEFAULT_MODE: all
8+
// FILE: Foo.kt
9+
10+
interface Foo<T> {
11+
fun foo(p: T) = p
12+
}
13+
14+
interface Foo2<T> {
15+
fun foo(p: T): T = null!!
16+
}
17+
18+
// MODULE: main(lib)
19+
// !JVM_DEFAULT_MODE: disable
20+
// !JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE
21+
// FILE: main.kt
22+
class DerivedClass : Foo<String>
23+
24+
interface DerivedInterface<T> : Foo2<T> {
25+
override fun foo(p: T) = p
26+
}
27+
28+
class DerivedClassWithSpecialization : DerivedInterface<String>
29+
30+
fun box(): String {
31+
return DerivedClass().foo("O") + DerivedClassWithSpecialization().foo("K")
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Module: lib
2+
@kotlin.Metadata
3+
public interface Foo {
4+
// source: 'Foo.kt'
5+
public method foo(p0: java.lang.Object): java.lang.Object
6+
}
7+
8+
@kotlin.Metadata
9+
public interface Foo2 {
10+
// source: 'Foo.kt'
11+
public method foo(p0: java.lang.Object): java.lang.Object
12+
}
13+
Module: main
14+
@kotlin.Metadata
15+
public final class DerivedClass {
16+
// source: 'main.kt'
17+
public method <init>(): void
18+
}
19+
20+
@kotlin.Metadata
21+
public final class DerivedClassWithSpecialization {
22+
// source: 'main.kt'
23+
public method <init>(): void
24+
public @org.jetbrains.annotations.NotNull method foo(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
25+
public synthetic bridge method foo(p0: java.lang.Object): java.lang.Object
26+
}
27+
28+
@kotlin.Metadata
29+
public final class DerivedInterface$DefaultImpls {
30+
// source: 'main.kt'
31+
public static method foo(@org.jetbrains.annotations.NotNull p0: DerivedInterface, p1: java.lang.Object): java.lang.Object
32+
public final inner class DerivedInterface$DefaultImpls
33+
}
34+
35+
@kotlin.Metadata
36+
public interface DerivedInterface {
37+
// source: 'main.kt'
38+
public abstract method foo(p0: java.lang.Object): java.lang.Object
39+
public final inner class DerivedInterface$DefaultImpls
40+
}
41+
42+
@kotlin.Metadata
43+
public final class MainKt {
44+
// source: 'main.kt'
45+
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// CHECK_BYTECODE_LISTING
2+
// TARGET_BACKEND: JVM
3+
// IGNORE_BACKEND_FIR: JVM_IR
4+
// JVM_TARGET: 1.8
5+
// WITH_RUNTIME
6+
// MODULE: lib
7+
// !JVM_DEFAULT_MODE: all
8+
// FILE: Foo.kt
9+
10+
interface Foo<T> {
11+
fun foo(p: T) = p
12+
}
13+
14+
// MODULE: main(lib)
15+
// !JVM_DEFAULT_MODE: disable
16+
// !JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE
17+
// FILE: main.kt
18+
interface DerivedInterface<T> : Foo<T>
19+
20+
class DerivedClass : DerivedInterface<String> {
21+
override fun foo(p: String) = super.foo(p)
22+
}
23+
24+
fun box(): String {
25+
return DerivedClass().foo("OK")
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Module: lib
2+
@kotlin.Metadata
3+
public interface Foo {
4+
// source: 'Foo.kt'
5+
public method foo(p0: java.lang.Object): java.lang.Object
6+
}
7+
Module: main
8+
@kotlin.Metadata
9+
public final class DerivedClass {
10+
// source: 'main.kt'
11+
public method <init>(): void
12+
public @org.jetbrains.annotations.NotNull method foo(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
13+
public synthetic bridge method foo(p0: java.lang.Object): java.lang.Object
14+
}
15+
16+
@kotlin.Metadata
17+
public interface DerivedInterface {
18+
// source: 'main.kt'
19+
}
20+
21+
@kotlin.Metadata
22+
public final class MainKt {
23+
// source: 'main.kt'
24+
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package base
2+
3+
interface UExpression {
4+
fun evaluate(): Any? = "fail"
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultNonDefaultInheritanceSuperCall/source.kt:5:22: error: interfaces can call JVM-default members via super only within JVM-default members. Please use '-Xjvm-default=all/all-compatibility' modes for such calls
2+
return super.evaluate()
3+
^
4+
compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultNonDefaultInheritanceSuperCall/source.kt:5:22: error: super calls of '@JvmDefault' members are only allowed with -Xjvm-default option
5+
return super.evaluate()
6+
^
7+
COMPILATION_ERROR
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import base.*
2+
3+
interface KotlinEvaluatableUElement : UExpression {
4+
override fun evaluate(): Any? {
5+
return super.evaluate()
6+
}
7+
}

0 commit comments

Comments
 (0)