diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java index ad367f92101..6fa20d9d54a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java @@ -51,12 +51,24 @@ public interface DataFlowInfo { Nullability getPredictableNullability(@NotNull DataFlowValue key); /** + * Returns possible types for the given value, NOT taking its predictability into account. + * * IMPORTANT: by default, the original (native) type for this value * are NOT included. So it's quite possible to get an empty set here. */ @NotNull Set getPossibleTypes(@NotNull DataFlowValue key); + /** + * Returns possible types for the given value if it's predictable. + * Otherwise, basic value type is returned. + * + * IMPORTANT: by default, the original (native) type for this value + * are NOT included. So it's quite possible to get an empty set here. + */ + @NotNull + Set getPredictableTypes(@NotNull DataFlowValue key); + /** * Call this function to clear all data flow information about * the given data flow value. Useful when we are not sure how this value can be changed, e.g. in a loop. diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java index 6dfad095bfa..869943be3b6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java @@ -24,10 +24,7 @@ import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL; @@ -145,7 +142,7 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL public Set getPossibleTypes(@NotNull DataFlowValue key) { KotlinType originalType = key.getType(); Set types = collectTypesFromMeAndParents(key); - if (getPredictableNullability(key).canBeNull()) { + if (getNullability(key).canBeNull()) { return types; } @@ -160,6 +157,15 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL return enrichedTypes; } + @Override + @NotNull + public Set getPredictableTypes(@NotNull DataFlowValue key) { + if (!key.isPredictable()) { + return new LinkedHashSet(); + } + return getPossibleTypes(key); + } + /** * Call this function to clear all data flow information about * the given data flow value. @@ -187,7 +193,7 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL putNullability(nullability, a, nullabilityOfB); SetMultimap newTypeInfo = newTypeInfo(); - Set typesForB = getPossibleTypes(b); + Set typesForB = getPredictableTypes(b); // Own type of B must be recorded separately, e.g. for a constant // But if its type is the same as A or it's null, there is no reason to do it // because usually null type or own type are not saved in this set @@ -216,8 +222,8 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA)); SetMultimap newTypeInfo = newTypeInfo(); - newTypeInfo.putAll(a, collectTypesFromMeAndParents(b)); - newTypeInfo.putAll(b, collectTypesFromMeAndParents(a)); + newTypeInfo.putAll(a, getPredictableTypes(b)); + newTypeInfo.putAll(b, getPredictableTypes(a)); if (!a.getType().equals(b.getType())) { // To avoid recording base types of own type if (!TypeUtilsKt.isSubtypeOf(a.getType(), b.getType())) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index ff487d46102..b5de274bbb9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -311,9 +311,7 @@ public class DataFlowAnalyzer { ) { DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c); Collection possibleTypes = Sets.newHashSet(type); - if (dataFlowValue.isPredictable()) { - possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue)); - } + possibleTypes.addAll(dataFlowInfo.getPredictableTypes(dataFlowValue)); return possibleTypes; } diff --git a/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt b/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt new file mode 100644 index 00000000000..5119d5d11b1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt @@ -0,0 +1,22 @@ +class Bar { + fun bar() {} +} + +class Foo(var x: Any) { + init { + if (x is Bar) { + val y = x + // Error: x is not stable, Type(y) = Any + x.bar() + y.bar() + if (y == x) { + // Still error + y.bar() + } + if (x !is Bar && y != x) { + // Still error + y.bar() + } + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.txt b/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.txt new file mode 100644 index 00000000000..56b30749b73 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.txt @@ -0,0 +1,17 @@ +package + +public final class Bar { + public constructor Bar() + public final fun bar(): kotlin.Unit + 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 Foo { + public constructor Foo(/*0*/ x: kotlin.Any) + public final var x: kotlin.Any + 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 8b4e1932f8f..d5b1d447f85 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -15066,6 +15066,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("unstableToStableTypes.kt") + public void testUnstableToStableTypes() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt"); + doTest(fileName); + } + @TestMetadata("varChangedInInitializer.kt") public void testVarChangedInInitializer() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.kt");