Skip to content

Commit

Permalink
JvmDefault. Allow non default inheritance with special flag
Browse files Browse the repository at this point in the history
 #KT-47000

(cherry picked from commit afc149d)
  • Loading branch information
Mikhael Bogdanov authored and Space committed Nov 9, 2021
1 parent 206d7d9 commit ddd02fe
Show file tree
Hide file tree
Showing 24 changed files with 464 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
)
var jvmDefault: String by FreezableVar(JvmDefaultMode.DEFAULT.description)

@Argument(value = "-Xjvm-default-allow-non-default-inheritance", description = "Allow inheritance from 'all*' modes for 'disable' one")
var jvmDefaultAllowDisableAgainstAll: Boolean by FreezableVar(false)

@Argument(
value = "-Xdefault-script-extension",
valueDescription = "<script filename extension>",
Expand Down Expand Up @@ -549,6 +552,7 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
result[AnalysisFlags.allowUnstableDependencies] = allowUnstableDependencies || useFir
result[JvmAnalysisFlags.disableUltraLightClasses] = disableUltraLightClasses
result[JvmAnalysisFlags.useIR] = !useOldBackend
result[JvmAnalysisFlags.jvmDefaultAllowNonDefaultInheritance] = jvmDefaultAllowDisableAgainstAll
return result
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ object JvmAnalysisFlags {
@JvmStatic
val jvmDefaultMode by Delegates.JvmDefaultModeDisabledByDefault

@JvmStatic
val jvmDefaultAllowNonDefaultInheritance by AnalysisFlag.Delegates.Boolean

@JvmStatic
val inheritMultifileParts by AnalysisFlag.Delegates.Boolean

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class JvmDefaultChecker(private val jvmTarget: JvmTarget, private val project: P

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

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

if (descriptor is ClassDescriptor) {
val hasDeclaredJvmDefaults =
descriptor.unsubstitutedMemberScope.getContributedDescriptors().filterIsInstance<CallableMemberDescriptor>().any {
it.kind.isReal && it.isCompiledToJvmDefault(jvmDefaultMode)
if (!allowNonDefaultInheritance) {
if (descriptor is ClassDescriptor) {
val hasDeclaredJvmDefaults =
descriptor.unsubstitutedMemberScope.getContributedDescriptors().filterIsInstance<CallableMemberDescriptor>().any {
it.kind.isReal && it.isCompiledToJvmDefault(jvmDefaultMode)
}
if (!hasDeclaredJvmDefaults && !checkJvmDefaultsInHierarchy(descriptor, jvmDefaultMode)) {
context.trace.report(ErrorsJvm.JVM_DEFAULT_THROUGH_INHERITANCE.on(declaration))
}
if (!hasDeclaredJvmDefaults && !checkJvmDefaultsInHierarchy(descriptor, jvmDefaultMode)) {
context.trace.report(ErrorsJvm.JVM_DEFAULT_THROUGH_INHERITANCE.on(declaration))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class LanguageVersionSettingsBuilder {
analysisFlag(AnalysisFlags.allowKotlinPackage, trueOrNull(LanguageSettingsDirectives.ALLOW_KOTLIN_PACKAGE in directives)),

analysisFlag(JvmAnalysisFlags.jvmDefaultMode, directives.singleOrZeroValue(LanguageSettingsDirectives.JVM_DEFAULT_MODE)),
analysisFlag(JvmAnalysisFlags.jvmDefaultAllowNonDefaultInheritance, trueOrNull(LanguageSettingsDirectives.JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE in directives)),
analysisFlag(JvmAnalysisFlags.inheritMultifileParts, trueOrNull(LanguageSettingsDirectives.INHERIT_MULTIFILE_PARTS in directives)),
analysisFlag(JvmAnalysisFlags.sanitizeParentheses, trueOrNull(LanguageSettingsDirectives.SANITIZE_PARENTHESES in directives)),
analysisFlag(JvmAnalysisFlags.enableJvmPreview, trueOrNull(LanguageSettingsDirectives.ENABLE_JVM_PREVIEW in directives)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ object LanguageSettingsDirectives : SimpleDirectivesContainer() {
additionalParser = JvmDefaultMode.Companion::fromStringOrNull
)

val JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE by directive(
description = "Configures corresponding analysis flag (JvmAnalysisFlags.jvmDefaultAllowNonDefaultInheritance)",
)

val INHERIT_MULTIFILE_PARTS by directive(
description = "Enables corresponding analysis flag (JvmAnalysisFlags.inheritMultifileParts)"
)
Expand Down
2 changes: 2 additions & 0 deletions compiler/testData/cli/jvm/extraHelp.out
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ where advanced options include:
(annotating an existing method can break binary compatibility)
-Xjvm-default=compatibility Allow usages of @JvmDefault; generate a compatibility accessor
in the 'DefaultImpls' class in addition to the default interface method
-Xjvm-default-allow-non-default-inheritance
Allow inheritance from 'all*' modes for 'disable' one
-Xklib=<path> Paths to cross-platform libraries in .klib format
-Xlambdas={class|indy} Select code generation scheme for lambdas.
-Xlambdas=indy Generate lambdas using `invokedynamic` with `LambdaMetafactory.metafactory`. Requires `-jvm-target 1.8` or greater.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FULL_JDK
// JVM_TARGET: 1.8
// WITH_RUNTIME
// MODULE: lib
// !JVM_DEFAULT_MODE: all
// FILE: Foo.kt

interface Foo {
fun toOverride(): String = "fail"

fun nonOverride(): String = "K"
}

// MODULE: main(lib)
// !JVM_DEFAULT_MODE: disable
// !JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE
// FILE: main.kt

interface Derived : Foo {
override fun toOverride(): String {
return "O"
}
}

class DerivedClass : Derived


fun box(): String {
checkMethodExists(DerivedClass::class.java, "toOverride")
checkNoMethod(DerivedClass::class.java, "nonOverride")

val value = DerivedClass()
return value.toOverride() + value.nonOverride()
}

fun checkNoMethod(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) {
try {
clazz.getDeclaredMethod(name, *parameterTypes)
}
catch (e: NoSuchMethodException) {
return
}
throw AssertionError("fail: method $name was found in " + clazz)
}

fun checkMethodExists(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) {
try {
clazz.getDeclaredMethod(name, *parameterTypes)
return
}
catch (e: NoSuchMethodException) {
throw AssertionError("fail: method $name was not found in " + clazz, e)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FULL_JDK
// JVM_TARGET: 1.8
// WITH_RUNTIME
// MODULE: lib
// !JVM_DEFAULT_MODE: all
// FILE: Foo.kt

interface Foo {
fun toOverride(): List<String> = null!!

fun nonOverride(): List<String> = Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName }
}

// MODULE: main(lib)
// !JVM_DEFAULT_MODE: disable
// !JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE
// FILE: main.kt

interface Derived : Foo {
override fun toOverride() = Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName }
}

class DerivedClass : Derived


fun box(): String {
val override = DerivedClass().toOverride()
if (override[1] != "Derived\$DefaultImpls.toOverride") return "fail 1: ${override[1]}"
if (override[2] != "DerivedClass.toOverride") return "fail 2: ${override[2]}"
if (override[3] != "MainKt.box") return "fail 3: ${override[3]}"

val nonOverride = DerivedClass().nonOverride()
if (nonOverride[1] != "Foo.nonOverride") return "fail 3: ${nonOverride[1]}"
if (nonOverride[2] != "MainKt.box") return "fail 4: ${nonOverride[2]}"

return "OK"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// CHECK_BYTECODE_LISTING
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// JVM_TARGET: 1.8
// WITH_RUNTIME
// MODULE: lib
// !JVM_DEFAULT_MODE: all
// FILE: Foo.kt

interface Foo<T> {
fun foo(p: T) = p
}

interface Foo2<T> {
fun foo(p: T): T = null!!
}

// MODULE: main(lib)
// !JVM_DEFAULT_MODE: disable
// !JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE
// FILE: main.kt
class DerivedClass : Foo<String>

interface DerivedInterface<T> : Foo2<T> {
override fun foo(p: T) = p
}

class DerivedClassWithSpecialization : DerivedInterface<String>

fun box(): String {
return DerivedClass().foo("O") + DerivedClassWithSpecialization().foo("K")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
Module: lib
@kotlin.Metadata
public interface Foo {
// source: 'Foo.kt'
public method foo(p0: java.lang.Object): java.lang.Object
}

@kotlin.Metadata
public interface Foo2 {
// source: 'Foo.kt'
public method foo(p0: java.lang.Object): java.lang.Object
}
Module: main
@kotlin.Metadata
public final class DerivedClass {
// source: 'main.kt'
public method <init>(): void
}

@kotlin.Metadata
public final class DerivedClassWithSpecialization {
// source: 'main.kt'
public method <init>(): void
public @org.jetbrains.annotations.NotNull method foo(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
public synthetic bridge method foo(p0: java.lang.Object): java.lang.Object
}

@kotlin.Metadata
public final class DerivedInterface$DefaultImpls {
// source: 'main.kt'
public static method foo(@org.jetbrains.annotations.NotNull p0: DerivedInterface, p1: java.lang.Object): java.lang.Object
public final inner class DerivedInterface$DefaultImpls
}

@kotlin.Metadata
public interface DerivedInterface {
// source: 'main.kt'
public abstract method foo(p0: java.lang.Object): java.lang.Object
public final inner class DerivedInterface$DefaultImpls
}

@kotlin.Metadata
public final class MainKt {
// source: 'main.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// CHECK_BYTECODE_LISTING
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// JVM_TARGET: 1.8
// WITH_RUNTIME
// MODULE: lib
// !JVM_DEFAULT_MODE: all
// FILE: Foo.kt

interface Foo<T> {
fun foo(p: T) = p
}

// MODULE: main(lib)
// !JVM_DEFAULT_MODE: disable
// !JVM_DEFAULT_ALLOW_NON_DEFAULT_INHERITANCE
// FILE: main.kt
interface DerivedInterface<T> : Foo<T>

class DerivedClass : DerivedInterface<String> {
override fun foo(p: String) = super.foo(p)
}

fun box(): String {
return DerivedClass().foo("OK")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Module: lib
@kotlin.Metadata
public interface Foo {
// source: 'Foo.kt'
public method foo(p0: java.lang.Object): java.lang.Object
}
Module: main
@kotlin.Metadata
public final class DerivedClass {
// source: 'main.kt'
public method <init>(): void
public @org.jetbrains.annotations.NotNull method foo(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
public synthetic bridge method foo(p0: java.lang.Object): java.lang.Object
}

@kotlin.Metadata
public interface DerivedInterface {
// source: 'main.kt'
}

@kotlin.Metadata
public final class MainKt {
// source: 'main.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package base

interface UExpression {
fun evaluate(): Any? = "fail"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
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
return super.evaluate()
^
compiler/testData/compileKotlinAgainstCustomBinaries/jvmDefaultNonDefaultInheritanceSuperCall/source.kt:5:22: error: super calls of '@JvmDefault' members are only allowed with -Xjvm-default option
return super.evaluate()
^
COMPILATION_ERROR
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import base.*

interface KotlinEvaluatableUElement : UExpression {
override fun evaluate(): Any? {
return super.evaluate()
}
}
Loading

0 comments on commit ddd02fe

Please sign in to comment.