[FE] Support sealed classes and interfaces from java

KT-43551
KT-41215
This commit is contained in:
Dmitriy Novozhilov
2020-11-19 17:48:25 +03:00
committed by TeamCityServer
parent bdfb71b149
commit 7897bb6adb
9 changed files with 260 additions and 10 deletions
@@ -0,0 +1,44 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// ISSUE: KT-41215, KT-43551
// FILE: Base.java
public sealed class Base permits A, B {}
// FILE: A.java
public final class A extends Base {}
// FILE: B.java
public sealed class B extends Base permits B.C, B.D {
public static final class C implements B {}
public static non-sealed class D extends B {}
}
// FILE: main.kt
fun test_ok_1(base: Base) {
val x = when (base) {
is A -> 1
is B -> 2
}
}
fun test_ok_2(base: Base) {
val x = when (base) {
is A -> 1
is B.C -> 2
is B.D -> 3
}
}
fun test_error_1(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
}
}
fun test_error_2(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
is B.C -> 2
}
}
@@ -0,0 +1,41 @@
package
public fun test_error_1(/*0*/ base: Base): kotlin.Unit
public fun test_error_2(/*0*/ base: Base): kotlin.Unit
public fun test_ok_1(/*0*/ base: Base): kotlin.Unit
public fun test_ok_2(/*0*/ base: Base): kotlin.Unit
public final class A : Base {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public sealed class B : Base {
public constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final class C : B {
public constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class D : B {
public constructor D()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public sealed class Base {
public constructor Base()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,64 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// ISSUE: KT-41215, KT-43551
// FILE: Base.java
public sealed interface Base permits A, B, E {}
// FILE: A.java
public non-sealed interface A extends Base {}
// FILE: B.java
public sealed interface B extends Base permits B.C, B.D {
public static final class C implements B {}
public static non-sealed interface D extends B {}
}
// FILE: E.java
public enum E implements Base {
First, Second
}
// FILE: main.kt
fun test_ok_1(base: Base) {
val x = when (base) {
is A -> 1
is B -> 2
is E -> 3
}
}
fun test_ok_2(base: Base) {
val x = when (base) {
is A -> 1
is B.C -> 2
is B.D -> 3
E.First -> 4
E.Second -> 5
}
}
fun test_error_1(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
is B -> 2
}
}
fun test_error_2(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
is B.C -> 2
is B.D -> 3
E.Second -> 5
}
}
fun test_error_3(base: Base) {
val x = <!NO_ELSE_IN_WHEN!>when<!> (base) {
is A -> 1
is B.C -> 2
E.First -> 4
E.Second -> 5
}
}
@@ -0,0 +1,60 @@
package
public fun test_error_1(/*0*/ base: Base): kotlin.Unit
public fun test_error_2(/*0*/ base: Base): kotlin.Unit
public fun test_error_3(/*0*/ base: Base): kotlin.Unit
public fun test_ok_1(/*0*/ base: Base): kotlin.Unit
public fun test_ok_2(/*0*/ base: Base): kotlin.Unit
public interface A : Base {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public sealed interface B : Base {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final class C : B {
public constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface D : B {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public sealed interface Base {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final enum class E : kotlin.Enum<E!>, Base {
enum entry First
enum entry Second
public constructor E()
public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E!): kotlin.Int
@kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "This member is not fully supported by Kotlin compiler, so it may be absent or have different signature in next major version", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public final override /*1*/ /*fake_override*/ fun describeConstable(): java.util.Optional<java.lang.Enum.EnumDesc<E!>!>!
public final override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<E!>!
public final override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E
public final /*synthesized*/ fun values(): kotlin.Array<E>
}
@@ -70,4 +70,37 @@ public class DiagnosticsWithJdk15TestGenerated extends AbstractDiagnosticsWithJd
runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/supertypesCheck.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/testsWithJava15/sealedClasses")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SealedClasses extends AbstractDiagnosticsWithJdk15Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInSealedClasses() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJava15/sealedClasses"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("javaSealedClassExhaustiveness.kt")
public void testJavaSealedClassExhaustiveness() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJava15/sealedClasses/javaSealedClassExhaustiveness.kt");
}
@TestMetadata("javaSealedInterfaceExhaustiveness.kt")
public void testJavaSealedInterfaceExhaustiveness() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJava15/sealedClasses/javaSealedInterfaceExhaustiveness.kt");
}
@TestMetadata("kotlinInheritsJavaClass.kt")
public void testKotlinInheritsJavaClass() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaClass.kt");
}
@TestMetadata("kotlinInheritsJavaInterface.kt")
public void testKotlinInheritsJavaInterface() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJava15/sealedClasses/kotlinInheritsJavaInterface.kt");
}
}
}
@@ -15,12 +15,13 @@ enum class Modality {
ABSTRACT;
companion object {
// NB: never returns SEALED
fun convertFromFlags(abstract: Boolean, open: Boolean): Modality {
if (abstract) return ABSTRACT
if (open) return OPEN
return FINAL
fun convertFromFlags(sealed: Boolean, abstract: Boolean, open: Boolean): Modality {
return when {
sealed -> SEALED
abstract -> ABSTRACT
open -> OPEN
else -> FINAL
}
}
}
}
@@ -76,7 +76,7 @@ class LazyJavaClassDescriptor(
private val modality =
if (jClass.isAnnotationType || jClass.isEnum) Modality.FINAL
else Modality.convertFromFlags(jClass.isAbstract || jClass.isInterface, !jClass.isFinal)
else Modality.convertFromFlags(jClass.isSealed, jClass.isAbstract || jClass.isInterface, !jClass.isFinal)
private val visibility = jClass.visibility
private val isInner = jClass.outerClass != null && !jClass.isStatic
@@ -180,7 +180,14 @@ class LazyJavaClassDescriptor(
fun wasScopeContentRequested() =
getUnsubstitutedMemberScope().wasContentRequested() || staticScope.wasContentRequested()
override fun getSealedSubclasses(): Collection<ClassDescriptor> = emptyList()
override fun getSealedSubclasses(): Collection<ClassDescriptor> = if (modality == Modality.SEALED) {
val attributes = TypeUsage.COMMON.toAttributes()
jClass.permittedTypes.mapNotNull {
c.typeResolver.transformJavaType(it, attributes).constructor.declarationDescriptor as? ClassDescriptor
}
} else {
emptyList()
}
override fun toString() = "Lazy Java class ${this.fqNameUnsafe}"
@@ -515,7 +515,7 @@ class LazyJavaClassMemberScope(
returnType,
// Those functions are generated as open in bytecode
// Actually, it should not be important because the class is final anyway, but leaving them open is convenient for consistency
Modality.convertFromFlags(abstract = false, open = true),
Modality.convertFromFlags(sealed = false, abstract = false, open = true),
DescriptorVisibilities.PUBLIC,
null,
)
@@ -181,7 +181,7 @@ abstract class LazyJavaScope(
effectiveSignature.typeParameters,
effectiveSignature.valueParameters,
effectiveSignature.returnType,
Modality.convertFromFlags(method.isAbstract, !method.isFinal),
Modality.convertFromFlags(sealed = false, method.isAbstract, !method.isFinal),
method.visibility.toDescriptorVisibility(),
if (effectiveSignature.receiverType != null)
mapOf(JavaMethodDescriptor.ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER to valueParameters.descriptors.first())