diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index 0edb6b80eef..ce1d6ddf0cf 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -20768,6 +20768,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.kt"); } + @TestMetadata("ExhaustiveWithFreedom.kt") + public void testExhaustiveWithFreedom() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt"); + } + @TestMetadata("Local.kt") public void testLocal() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/Local.kt"); @@ -20788,6 +20793,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/sealed/NestedSealed.kt"); } + @TestMetadata("NestedSealedWithoutRestrictions.kt") + public void testNestedSealedWithoutRestrictions() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.kt"); + } + @TestMetadata("NeverConstructed.kt") public void testNeverConstructed() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/NeverConstructed.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java index e0de663ae1d..02e41a83ad7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java @@ -271,7 +271,8 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes ); // TODO: only consider classes from the same file, not the whole package fragment - this.sealedSubclasses = storageManager.createLazyValue(() -> DescriptorUtilsKt.computeSealedSubclasses(this)); + boolean freedomForSealedInterfacesSupported = c.getLanguageVersionSettings().supportsFeature(LanguageFeature.FreedomForSealedClasses); + this.sealedSubclasses = storageManager.createLazyValue(() -> DescriptorUtilsKt.computeSealedSubclasses(this, freedomForSealedInterfacesSupported)); } private static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) { diff --git a/compiler/testData/codegen/box/sealed/multipleFiles_enabled.kt b/compiler/testData/codegen/box/sealed/multipleFiles_enabled.kt index e975dbe9924..bdae46a7afe 100644 --- a/compiler/testData/codegen/box/sealed/multipleFiles_enabled.kt +++ b/compiler/testData/codegen/box/sealed/multipleFiles_enabled.kt @@ -2,23 +2,40 @@ // IGNORE_BACKEND_FIR: JVM_IR // !LANGUAGE: +FreedomForSealedClasses -// FILE: a.kt +// FILE: Base.kt sealed class Base { class A : Base() } -// FILE: b.kt +// FILE: B.kt class B : Base() -// FILE: c.kt +// FILE: Container.kt -fun getLetter(base: Base): String = when (base) { - is Base.A -> "O" - is B -> "K" +class Containter { + class C : Base() + + inner class D : Base() + + val d = D() +} + +// FILE: main.kt + +fun getValue(base: Base): Int = when (base) { + is Base.A -> 1 + is B -> 2 + is Containter.C -> 3 + is Containter.D -> 4 } fun box(): String { - return getLetter(Base.A()) + getLetter(B()) + var res = 0 + res += getValue(Base.A()) + res += getValue(B()) + res += getValue(Containter.C()) + res += getValue(Containter().d) + return if (res == 10) "OK" else "Fail" } diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.fir.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.fir.kt new file mode 100644 index 00000000000..171f5f2e107 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.fir.kt @@ -0,0 +1,64 @@ +// ISSUE: KT-13495 +// !DIAGNOSTICS: -UNUSED_VARIABLE +// !LANGUAGE: +FreedomForSealedClasses + +// FILE: Base.kt + +sealed class Base { + class A : Base() +} + +// FILE: B.kt + +class B : Base() + +// FILE: Container.kt + +class Containter { + class C : Base() + + inner class D : Base() +} + +// FILE: main.kt + +fun test_OK(base: Base) { + val x = when (base) { + is Base.A -> 1 + is B -> 2 + is Containter.C -> 3 + is Containter.D -> 4 + } +} + +fun test_error_1(base: Base) { + val x = when (base) { + is B -> 2 + is Containter.C -> 3 + is Containter.D -> 4 + } +} + +fun test_error_2(base: Base) { + val x = when (base) { + is Base.A -> 1 + is Containter.C -> 3 + is Containter.D -> 4 + } +} + +fun test_error_3(base: Base) { + val x = when (base) { + is Base.A -> 1 + is B -> 2 + is Containter.D -> 4 + } +} + +fun test_error_4(base: Base) { + val x = when (base) { + is Base.A -> 1 + is B -> 2 + is Containter.C -> 3 + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt new file mode 100644 index 00000000000..fc58d0236cc --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt @@ -0,0 +1,64 @@ +// ISSUE: KT-13495 +// !DIAGNOSTICS: -UNUSED_VARIABLE +// !LANGUAGE: +FreedomForSealedClasses + +// FILE: Base.kt + +sealed class Base { + class A : Base() +} + +// FILE: B.kt + +class B : Base() + +// FILE: Container.kt + +class Containter { + class C : Base() + + inner class D : Base() +} + +// FILE: main.kt + +fun test_OK(base: Base) { + val x = when (base) { + is Base.A -> 1 + is B -> 2 + is Containter.C -> 3 + is Containter.D -> 4 + } +} + +fun test_error_1(base: Base) { + val x = when (base) { + is B -> 2 + is Containter.C -> 3 + is Containter.D -> 4 + } +} + +fun test_error_2(base: Base) { + val x = when (base) { + is Base.A -> 1 + is Containter.C -> 3 + is Containter.D -> 4 + } +} + +fun test_error_3(base: Base) { + val x = when (base) { + is Base.A -> 1 + is B -> 2 + is Containter.D -> 4 + } +} + +fun test_error_4(base: Base) { + val x = when (base) { + is Base.A -> 1 + is B -> 2 + is Containter.C -> 3 + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.txt b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.txt new file mode 100644 index 00000000000..ca2a9591379 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.txt @@ -0,0 +1,49 @@ +package + +public fun test_OK(/*0*/ base: Base): kotlin.Unit +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_error_4(/*0*/ base: Base): kotlin.Unit + +public final 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 sealed class Base { + internal 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 + + 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 final class Containter { + public constructor Containter() + 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 : Base { + 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 final inner class D : Base { + 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 + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.fir.kt b/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.fir.kt index a490bda54c5..fb520120ea8 100644 --- a/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.fir.kt +++ b/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.fir.kt @@ -1,5 +1,4 @@ // ISSUE: KT-13495 -// !DIAGNOSTICS: -UNUSED_VARIABLE // !LANGUAGE: +FreedomForSealedClasses // FILE: a.kt @@ -25,14 +24,3 @@ class Container { class LocalClass : Base() {} // Should be an error } } - -// FILE: d.kt - -fun test(base: Base) { - val x = when (base) { - is Base.A -> 1 - is B -> 2 - is Container.C -> 3 - is Container.D -> 4 - } -} diff --git a/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.kt b/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.kt index 267ac83315a..b54b8f82f2c 100644 --- a/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.kt +++ b/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.kt @@ -1,5 +1,4 @@ // ISSUE: KT-13495 -// !DIAGNOSTICS: -UNUSED_VARIABLE // !LANGUAGE: +FreedomForSealedClasses // FILE: a.kt @@ -25,14 +24,3 @@ class Container { class LocalClass : Base() {} // Should be an error } } - -// FILE: d.kt - -fun test(base: Base) { - val x = when (base) { - is Base.A -> 1 - is B -> 2 - is Container.C -> 3 - is Container.D -> 4 - } -} diff --git a/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.txt b/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.txt index 0a71f3f94b6..51fcecaca3b 100644 --- a/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.txt +++ b/compiler/testData/diagnostics/tests/sealed/MultipleFiles_enabled.txt @@ -1,7 +1,5 @@ package -public fun test(/*0*/ base: Base): kotlin.Unit - public final class B : Base { public constructor B() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.fir.kt b/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.fir.kt new file mode 100644 index 00000000000..444fb416c12 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.fir.kt @@ -0,0 +1,39 @@ +// ISSUE: KT-13495 +// !LANGUAGE: +FreedomForSealedClasses +// !DIAGNOSTICS: -UNUSED_VARIABLE + +// FILE: base.kt + +package foo + +class Container { + sealed class Base +} + +// FILE: a.kt + +package foo + +class A : Container.Base() + +// FILE: b.kt + +package foo + +class BContainer { + class B : Container.Base() + + inner class C : Container.Base() +} + +// FILE: test.kt + +package foo + +fun test(base: Container.Base) { + val x = when (base) { + is A -> 1 + is BContainer.B -> 2 + is BContainer.C -> 3 + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.kt b/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.kt new file mode 100644 index 00000000000..fdd2bdcb6a8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.kt @@ -0,0 +1,39 @@ +// ISSUE: KT-13495 +// !LANGUAGE: +FreedomForSealedClasses +// !DIAGNOSTICS: -UNUSED_VARIABLE + +// FILE: base.kt + +package foo + +class Container { + sealed class Base +} + +// FILE: a.kt + +package foo + +class A : Container.Base() + +// FILE: b.kt + +package foo + +class BContainer { + class B : Container.Base() + + inner class C : Container.Base() +} + +// FILE: test.kt + +package foo + +fun test(base: Container.Base) { + val x = when (base) { + is A -> 1 + is BContainer.B -> 2 + is BContainer.C -> 3 + } +} diff --git a/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.txt b/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.txt new file mode 100644 index 00000000000..319749dd71b --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.txt @@ -0,0 +1,47 @@ +package + +package foo { + public fun test(/*0*/ base: foo.Container.Base): kotlin.Unit + + public final class A : foo.Container.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 final class BContainer { + public constructor BContainer() + 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 B : foo.Container.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 inner class C : foo.Container.Base { + 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 final class Container { + public constructor Container() + 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 { + internal 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 + } + } +} diff --git a/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 111cc4e95a3..5c1521aa986 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -20845,6 +20845,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.kt"); } + @TestMetadata("ExhaustiveWithFreedom.kt") + public void testExhaustiveWithFreedom() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt"); + } + @TestMetadata("Local.kt") public void testLocal() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/Local.kt"); @@ -20865,6 +20870,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/sealed/NestedSealed.kt"); } + @TestMetadata("NestedSealedWithoutRestrictions.kt") + public void testNestedSealedWithoutRestrictions() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.kt"); + } + @TestMetadata("NeverConstructed.kt") public void testNeverConstructed() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/NeverConstructed.kt"); diff --git a/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 7be16516432..6b56a052b0f 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -20770,6 +20770,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWhenWithElse.kt"); } + @TestMetadata("ExhaustiveWithFreedom.kt") + public void testExhaustiveWithFreedom() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt"); + } + @TestMetadata("Local.kt") public void testLocal() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/Local.kt"); @@ -20790,6 +20795,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/sealed/NestedSealed.kt"); } + @TestMetadata("NestedSealedWithoutRestrictions.kt") + public void testNestedSealedWithoutRestrictions() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/NestedSealedWithoutRestrictions.kt"); + } + @TestMetadata("NeverConstructed.kt") public void testNeverConstructed() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/NeverConstructed.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index d88800d8032..7577fcf601f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -377,7 +377,7 @@ fun ClassifierDescriptor.getAllSuperClassifiers(): Sequence { +fun computeSealedSubclasses(sealedClass: ClassDescriptor, freedomForSealedInterfacesSupported: Boolean): Collection { if (sealedClass.modality != Modality.SEALED) return emptyList() val result = linkedSetOf() @@ -396,9 +396,16 @@ fun computeSealedSubclasses(sealedClass: ClassDescriptor): Collection