diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt index 248b6e22c43..0203999e4a1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt @@ -410,7 +410,13 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping possibleTypesForSubject: Set ) { val subjectExpression = expression.subjectExpression ?: return - for (possibleCastType in possibleTypesForSubject) { + // Using "reversed()" here is a kind of hack to fix KT-27221 + // KT-27221 was a breaking change introduced after DataFlowImpl optimization that changed the order of possible types to reversed, + // and it led to the wrong sealed class being chosen. + // But it seems that order of collected types shouldn't matter at all + // (at least because the order of relevant checks might change the behavior, see KT-27252) + // TODO: Read the comment above, wait for resolution in KT-27252 and get rid of "reversed" call here + for (possibleCastType in possibleTypesForSubject.reversed()) { val possibleCastClass = possibleCastType.constructor.declarationDescriptor as? ClassDescriptor ?: continue if (possibleCastClass.kind == ClassKind.ENUM_CLASS || possibleCastClass.modality == Modality.SEALED) { if (checkSmartCastToExpectedTypeInSubject( diff --git a/compiler/testData/diagnostics/tests/smartCasts/kt27221.kt b/compiler/testData/diagnostics/tests/smartCasts/kt27221.kt new file mode 100644 index 00000000000..c234137cced --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/kt27221.kt @@ -0,0 +1,28 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE +// SKIP_TXT + +sealed class A +sealed class B : A() +sealed class C : B() +object BB : B() +object CC : C() + +fun foo(a: A) { + if (a is B) { + if (a is C) { + val t = when (a) { + is CC -> "CC" + } + } + } +} + +fun foo2(a: A) { + if (a is C) { + if (a is B) { + val t = when (a) { + is CC -> "CC" + } + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/kt27221_2.kt b/compiler/testData/diagnostics/tests/smartCasts/kt27221_2.kt new file mode 100644 index 00000000000..c209afa95a2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/kt27221_2.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE +// SKIP_TXT + +sealed class A +sealed class B : A() +sealed class C : B() +sealed class D : C() +object BB : B() +object CC : C() +object DD : D() + +fun foo1(a: A) { + if (a is B) { + if (a is D) { + if (a is C) { + val t = + when (a) { + is DD -> "DD" + } + } + } + } +} + +fun foo2(a: A) { + if (a is B) { + if (a is D) { + if (a is C) { + val t = + when (a) { + is DD -> "DD" + } + } + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/kt27221_irrelevantClasses.kt b/compiler/testData/diagnostics/tests/smartCasts/kt27221_irrelevantClasses.kt new file mode 100644 index 00000000000..3a117080867 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/kt27221_irrelevantClasses.kt @@ -0,0 +1,28 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE +// SKIP_TXT + +sealed class A +sealed class B : A() +sealed class C : A() +object BB : B() +object CC : C() + +fun foo(a: A) { + if (a is B) { + if (a is C) { + val t = when (a) { + is CC -> "CC" + } + } + } +} + +fun foo2(a: A) { + if (a is C) { + if (a is B) { + val t = when (a) { + is CC -> "CC" + } + } + } +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 08ffa1ca2ab..5ceed36006f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -19117,6 +19117,21 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/smartCasts/kt2422.kt"); } + @TestMetadata("kt27221.kt") + public void testKt27221() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt27221.kt"); + } + + @TestMetadata("kt27221_2.kt") + public void testKt27221_2() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt27221_2.kt"); + } + + @TestMetadata("kt27221_irrelevantClasses.kt") + public void testKt27221_irrelevantClasses() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt27221_irrelevantClasses.kt"); + } + @TestMetadata("kt2865.kt") public void testKt2865() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/kt2865.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 5dc75534483..7ac4d1a4e53 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -19117,6 +19117,21 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/smartCasts/kt2422.kt"); } + @TestMetadata("kt27221.kt") + public void testKt27221() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt27221.kt"); + } + + @TestMetadata("kt27221_2.kt") + public void testKt27221_2() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt27221_2.kt"); + } + + @TestMetadata("kt27221_irrelevantClasses.kt") + public void testKt27221_irrelevantClasses() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt27221_irrelevantClasses.kt"); + } + @TestMetadata("kt2865.kt") public void testKt2865() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/kt2865.kt");