From 2c02a9b982f53a05b1891e9a09338adecde4d54c Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Fri, 10 Jun 2011 20:13:06 +0400 Subject: [PATCH] Info extraction for is-conditions in if's --- .../jet/lang/types/DataFlowInfo.java | 72 +++++++++++++++---- .../jet/lang/types/JetTypeInferrer.java | 41 ++++++++++- 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/idea/src/org/jetbrains/jet/lang/types/DataFlowInfo.java b/idea/src/org/jetbrains/jet/lang/types/DataFlowInfo.java index 761abb986f9..c78ce25faed 100644 --- a/idea/src/org/jetbrains/jet/lang/types/DataFlowInfo.java +++ b/idea/src/org/jetbrains/jet/lang/types/DataFlowInfo.java @@ -1,19 +1,25 @@ package org.jetbrains.jet.lang.types; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; +import com.google.common.base.Supplier; +import com.google.common.collect.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.VariableDescriptor; -import java.util.Map; +import java.util.*; /** * @author abreslav */ public class DataFlowInfo { - private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.of()); + private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.of(), Multimaps.forMap(Collections.emptyMap())); + public static final Supplier> SORTED_SET_SUPPLIER = new Supplier>() { + @Override + public SortedSet get() { + return new TreeSet(); + } + }; public static DataFlowInfo getEmpty() { return EMPTY; @@ -40,9 +46,11 @@ public class DataFlowInfo { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private final ImmutableMap nullabilityInfo; + private final Multimap typeInfo; - public DataFlowInfo(ImmutableMap nullabilityInfo) { + public DataFlowInfo(ImmutableMap nullabilityInfo, Multimap typeInfo) { this.nullabilityInfo = nullabilityInfo; + this.typeInfo = typeInfo; } @Nullable @@ -57,27 +65,51 @@ public class DataFlowInfo { return outType; } + @NotNull + public Collection getPossibleTypes(VariableDescriptor variableDescriptor) { + return typeInfo.get(variableDescriptor); + } + public DataFlowInfo equalsToNull(@NotNull VariableDescriptor variableDescriptor, boolean notNull) { + return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, notNull), typeInfo); + } + + private ImmutableMap getEqualsToNullMap(VariableDescriptor variableDescriptor, boolean notNull) { Map builder = Maps.newHashMap(nullabilityInfo); builder.put(variableDescriptor, new NullabilityFlags(!notNull, notNull)); - return new DataFlowInfo(ImmutableMap.copyOf(builder)); + return ImmutableMap.copyOf(builder); + } + + public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor variableDescriptor, @NotNull JetType type) { + Multimap newTypeInfo = copyTypeInfo(); + newTypeInfo.put(variableDescriptor, type); + return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, false), newTypeInfo); } public DataFlowInfo and(DataFlowInfo other) { - Map builder = Maps.newHashMap(); - builder.putAll(nullabilityInfo); + Map nullabilityMapBuilder = Maps.newHashMap(); + nullabilityMapBuilder.putAll(nullabilityInfo); for (Map.Entry entry : other.nullabilityInfo.entrySet()) { VariableDescriptor variableDescriptor = entry.getKey(); NullabilityFlags otherFlags = entry.getValue(); NullabilityFlags thisFlags = nullabilityInfo.get(variableDescriptor); if (thisFlags != null) { - builder.put(variableDescriptor, thisFlags.and(otherFlags)); + nullabilityMapBuilder.put(variableDescriptor, thisFlags.and(otherFlags)); } else { - builder.put(variableDescriptor, otherFlags); + nullabilityMapBuilder.put(variableDescriptor, otherFlags); } } - return new DataFlowInfo(ImmutableMap.copyOf(builder)); + + Multimap newTypeInfo = copyTypeInfo(); + newTypeInfo.putAll(other.typeInfo); + return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo); + } + + private Multimap copyTypeInfo() { + Multimap newTypeInfo = Multimaps.newSortedSetMultimap(Maps.>newHashMap(), SORTED_SET_SUPPLIER); + newTypeInfo.putAll(typeInfo); + return newTypeInfo; } public DataFlowInfo or(DataFlowInfo other) { @@ -90,7 +122,23 @@ public class DataFlowInfo { assert (otherFlags != null); builder.put(variableDescriptor, thisFlags.or(otherFlags)); } - return new DataFlowInfo(ImmutableMap.copyOf(builder)); + + Multimap newTypeInfo = copyTypeInfo(); + + Set keys = newTypeInfo.keySet(); + keys.retainAll(other.typeInfo.keySet()); + + for (VariableDescriptor variableDescriptor : keys) { + Collection thisTypes = typeInfo.get(variableDescriptor); + Collection otherTypes = other.typeInfo.get(variableDescriptor); + + Collection newTypes = Sets.newHashSet(thisTypes); + newTypes.retainAll(otherTypes); + + newTypeInfo.putAll(variableDescriptor, newTypes); + } + + return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo); } private static class NullabilityFlags { diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index d7abc4b7f30..f80d4c7aaa2 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -1344,6 +1344,18 @@ public class JetTypeInferrer { if (condition == null) return dataFlowInfo; final DataFlowInfo[] result = new DataFlowInfo[] {dataFlowInfo}; condition.accept(new JetVisitor() { + @Override + public void visitIsExpression(JetIsExpression expression) { + VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression.getLeftHandSide()); + if (variableDescriptor != null) { + JetPattern pattern = expression.getPattern(); + if (pattern instanceof JetTypePattern) { + JetTypePattern jetTypePattern = (JetTypePattern) pattern; + result[0] = dataFlowInfo.isInstanceOf(variableDescriptor, trace.getBindingContext().resolveTypeReference(jetTypePattern.getTypeReference())); + } + } + } + @Override public void visitBinaryExpression(JetBinaryExpression expression) { IElementType operationToken = expression.getOperationToken(); @@ -1612,6 +1624,20 @@ public class JetTypeInferrer { JetType receiverType = new TypeInferrerVisitorWithNamespaces(scope, false, dataFlowInfo).getType(receiverExpression); if (receiverType != null) { JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression); + + if (selectorReturnType == null) { + VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(receiverExpression); + if (variableDescriptor != null) { + Collection possibleTypes = dataFlowInfo.getPossibleTypes(variableDescriptor); + for (JetType possibleType : possibleTypes) { + selectorReturnType = getSelectorReturnType(possibleType, selectorExpression); + if (selectorReturnType != null) { + break; + } + } + } + } + if (expression.getOperationSign() == JetTokens.QUEST) { if (selectorReturnType != null && !isBoolean(selectorReturnType) && selectorExpression != null) { // TODO : more comprehensible error message @@ -1637,15 +1663,24 @@ public class JetTypeInferrer { } private JetType enrichOutType(JetExpression receiverExpression, JetType receiverType) { + VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(receiverExpression); + if (variableDescriptor != null) { + return dataFlowInfo.getOutType(variableDescriptor); + } + return receiverType; + } + + @Nullable + private VariableDescriptor getVariableDescriptorFromSimpleName(@NotNull JetExpression receiverExpression) { + VariableDescriptor variableDescriptor = null; if (receiverExpression instanceof JetSimpleNameExpression) { JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiverExpression; DeclarationDescriptor declarationDescriptor = trace.getBindingContext().resolveReferenceExpression(nameExpression); if (declarationDescriptor instanceof VariableDescriptor) { - VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor; - return dataFlowInfo.getOutType(variableDescriptor); + variableDescriptor = (VariableDescriptor) declarationDescriptor; } } - return receiverType; + return variableDescriptor; } @NotNull