DFA bug fix: assignment of unstable to stable uses predictable type set (see test)

This commit is contained in:
Mikhail Glukhikh
2015-12-07 12:17:29 +03:00
parent 2e73bcb3a6
commit 5c9e55f3fb
6 changed files with 72 additions and 11 deletions
@@ -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<KotlinType> 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<KotlinType> 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.
@@ -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<KotlinType> getPossibleTypes(@NotNull DataFlowValue key) {
KotlinType originalType = key.getType();
Set<KotlinType> 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<KotlinType> getPredictableTypes(@NotNull DataFlowValue key) {
if (!key.isPredictable()) {
return new LinkedHashSet<KotlinType>();
}
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<DataFlowValue, KotlinType> newTypeInfo = newTypeInfo();
Set<KotlinType> typesForB = getPossibleTypes(b);
Set<KotlinType> 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<DataFlowValue, KotlinType> 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())) {
@@ -311,9 +311,7 @@ public class DataFlowAnalyzer {
) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
Collection<KotlinType> possibleTypes = Sets.newHashSet(type);
if (dataFlowValue.isPredictable()) {
possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
}
possibleTypes.addAll(dataFlowInfo.getPredictableTypes(dataFlowValue));
return possibleTypes;
}
@@ -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
<!SMARTCAST_IMPOSSIBLE!>x<!>.bar()
y.<!UNRESOLVED_REFERENCE!>bar<!>()
if (y == x) {
// Still error
y.<!UNRESOLVED_REFERENCE!>bar<!>()
}
if (x !is Bar && y != x) {
// Still error
y.<!UNRESOLVED_REFERENCE!>bar<!>()
}
}
}
}
@@ -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
}
@@ -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");