From c73f01927a3014768d6cddbcf1ef65f1acc99b00 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 7 Dec 2015 19:08:30 +0300 Subject: [PATCH] 'sealed' is now not 'final' by default + related code changes #KT-10266 Fixed --- .../org/jetbrains/kotlin/cfg/WhenChecker.java | 3 +- .../kotlin/resolve/BodyResolver.java | 6 ++-- .../kotlin/resolve/DescriptorResolver.java | 4 --- .../PatternMatchingTypingVisitor.java | 2 +- .../NonExhaustiveWhenWithAdditionalCase.kt | 2 +- .../diagnostics/tests/sealed/WithInterface.kt | 10 ++++++ .../tests/sealed/WithInterface.txt | 36 +++++++++++++++++++ .../tests/smartCasts/openInSealed.kt | 8 +++++ .../tests/smartCasts/openInSealed.txt | 9 +++++ .../checkers/DiagnosticsTestGenerated.java | 12 +++++++ .../jetbrains/kotlin/descriptors/Modality.kt | 6 ++-- 11 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/sealed/WithInterface.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/WithInterface.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/openInSealed.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/openInSealed.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.java index 2b39973ba75..e3e79e5a315 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.java @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.descriptors.ClassKind; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.Modality; import org.jetbrains.kotlin.diagnostics.Errors; -import org.jetbrains.kotlin.lexer.KtToken; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; @@ -60,7 +59,7 @@ public final class WhenChecker { if (type == null) return null; ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type); if (classDescriptor == null) return null; - if (classDescriptor.getKind() != ClassKind.ENUM_CLASS || classDescriptor.getModality().isOverridable()) return null; + if (classDescriptor.getKind() != ClassKind.ENUM_CLASS) return null; return classDescriptor; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index 3cb6e6cb2f1..784759efae7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -488,8 +488,8 @@ public class BodyResolver { trace.report(SINGLETON_IN_SUPERTYPE.on(typeReference)); } } - else if (constructor.isFinal() && !allowedFinalSupertypes.contains(constructor)) { - if (classDescriptor.getModality() == Modality.SEALED) { + else if (!allowedFinalSupertypes.contains(constructor)) { + if (classDescriptor != null && classDescriptor.getModality() == Modality.SEALED) { DeclarationDescriptor containingDescriptor = supertypeOwner.getContainingDeclaration(); while (containingDescriptor != null && containingDescriptor != classDescriptor) { containingDescriptor = containingDescriptor.getContainingDeclaration(); @@ -501,7 +501,7 @@ public class BodyResolver { trace.report(SEALED_SUPERTYPE_IN_LOCAL_CLASS.on(typeReference)); } } - else { + else if (constructor.isFinal()) { trace.report(FINAL_SUPERTYPE.on(typeReference)); } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index deb4224fccf..169facc8ebe 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -614,10 +614,6 @@ public class DescriptorResolver { ) { if (DeclarationsCheckerKt.checkNotEnumEntry(upperBound, trace)) return; if (!TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, upperBoundType)) { - ClassifierDescriptor descriptor = upperBoundType.getConstructor().getDeclarationDescriptor(); - if (descriptor instanceof ClassDescriptor) { - if (((ClassDescriptor) descriptor).getModality() == Modality.SEALED) return; - } trace.report(FINAL_UPPER_BOUND.on(upperBound, upperBoundType)); } if (DynamicTypesKt.isDynamic(upperBoundType)) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java index 653a57ad1db..e27bc5a76c1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java @@ -345,7 +345,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { /* * (a: SubjectType) is Type */ - private void checkTypeCompatibility( + private static void checkTypeCompatibility( @NotNull ExpressionTypingContext context, @Nullable KotlinType type, @NotNull KotlinType subjectType, diff --git a/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.kt b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.kt index cae5f68b039..d8f20b9c3fc 100644 --- a/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.kt +++ b/compiler/testData/diagnostics/tests/sealed/NonExhaustiveWhenWithAdditionalCase.kt @@ -15,7 +15,7 @@ sealed class Sealed(val x: Int) { fun foo(s: Sealed): Int { return when(s) { is Sealed.First -> 1 - !is Sealed.ITuple -> 0 + !is Sealed.ITuple -> 0 // else required } } diff --git a/compiler/testData/diagnostics/tests/sealed/WithInterface.kt b/compiler/testData/diagnostics/tests/sealed/WithInterface.kt new file mode 100644 index 00000000000..d1b2b3641d0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/WithInterface.kt @@ -0,0 +1,10 @@ +interface Parent +interface Child : Parent + +sealed class Page : Parent { + object One : Page(), Child + object Two : Page(), Child +} + +// Ok: page is a Parent so it can be easily a Child +fun test(page: Page): Boolean = page is Child diff --git a/compiler/testData/diagnostics/tests/sealed/WithInterface.txt b/compiler/testData/diagnostics/tests/sealed/WithInterface.txt new file mode 100644 index 00000000000..755910f4b4e --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/WithInterface.txt @@ -0,0 +1,36 @@ +package + +public fun test(/*0*/ page: Page): kotlin.Boolean + +public interface Child : Parent { + 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 Page : Parent { + private constructor Page() + 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 object One : Page, Child { + private constructor One() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public object Two : Page, Child { + private constructor Two() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public interface Parent { + 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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/openInSealed.kt b/compiler/testData/diagnostics/tests/smartCasts/openInSealed.kt new file mode 100644 index 00000000000..70a19484fba --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/openInSealed.kt @@ -0,0 +1,8 @@ +sealed class My(open val x: Int?) { + init { + if (x != null) { + // Should be error: property is open + x.hashCode() + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/openInSealed.txt b/compiler/testData/diagnostics/tests/smartCasts/openInSealed.txt new file mode 100644 index 00000000000..ec78d0d7a29 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/openInSealed.txt @@ -0,0 +1,9 @@ +package + +public sealed class My { + private constructor My(/*0*/ x: kotlin.Int?) + public open val x: kotlin.Int? + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index d4a611f6091..4ce0faf7239 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -14300,6 +14300,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/WhenOnEmptySealed.kt"); doTest(fileName); } + + @TestMetadata("WithInterface.kt") + public void testWithInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/WithInterface.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/secondaryConstructors") @@ -15132,6 +15138,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("openInSealed.kt") + public void testOpenInSealed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/openInSealed.kt"); + doTest(fileName); + } + @TestMetadata("ownerDeclaresBothModifies.kt") public void testOwnerDeclaresBothModifies() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/Modality.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/Modality.kt index 9deac84b966..711034d9806 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/Modality.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/Modality.kt @@ -16,10 +16,12 @@ package org.jetbrains.kotlin.descriptors -enum class Modality private constructor(@get:JvmName("isOverridable") val isOverridable: Boolean) { +// For sealed classes, isOverridable is false but isOverridableByMembers is true +enum class Modality private constructor(val isOverridable: Boolean) { // THE ORDER OF ENTRIES MATTERS HERE FINAL(false), - SEALED(false), + // NB: class can be sealed but not function or property + SEALED(true), OPEN(true), ABSTRACT(true);