diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java index 888928f21b9..d2e390c93c7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java @@ -17,20 +17,14 @@ package org.jetbrains.jet.lang.resolve.calls.autocasts; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Multimaps; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.util.CommonSuppliers; -import java.util.Collection; -import java.util.Collections; import java.util.List; public interface DataFlowInfo { - public static final DataFlowInfo EMPTY = new DataFlowInfoImpl( - ImmutableMap.of(), - Multimaps.newListMultimap(Collections.>emptyMap(), - CommonSuppliers.getArrayListSupplier())); + DataFlowInfo EMPTY = new DelegatingDataFlowInfo(null, ImmutableMap.of(), + DelegatingDataFlowInfo.newTypeInfo()); @NotNull Nullability getNullability(@NotNull DataFlowValue key); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfoImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DelegatingDataFlowInfo.java similarity index 68% rename from compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfoImpl.java rename to compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DelegatingDataFlowInfo.java index 392f7dad6ef..76f10f90c80 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfoImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DelegatingDataFlowInfo.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.lang.resolve.calls.autocasts; import com.google.common.collect.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.util.CommonSuppliers; @@ -29,15 +30,25 @@ import java.util.Set; import static org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability.NOT_NULL; -public class DataFlowInfoImpl implements DataFlowInfo { +/* package */ class DelegatingDataFlowInfo implements DataFlowInfo { + @Nullable + private final DataFlowInfo parent; + + @NotNull private final ImmutableMap nullabilityInfo; + /** Also immutable */ + @NotNull private final ListMultimap typeInfo; - /* package */ DataFlowInfoImpl( + private static final ListMultimap EMPTY_TYPE_INFO = newTypeInfo(); + + /* package */ DelegatingDataFlowInfo( + @Nullable DataFlowInfo parent, @NotNull ImmutableMap nullabilityInfo, @NotNull ListMultimap typeInfo ) { + this.parent = parent; this.nullabilityInfo = nullabilityInfo; this.typeInfo = typeInfo; } @@ -47,87 +58,98 @@ public class DataFlowInfoImpl implements DataFlowInfo { public Nullability getNullability(@NotNull DataFlowValue key) { if (!key.isStableIdentifier()) return key.getImmanentNullability(); Nullability nullability = nullabilityInfo.get(key); - if (nullability == null) { - nullability = key.getImmanentNullability(); - } - return nullability; + return nullability != null ? nullability : + parent != null ? parent.getNullability(key) : + key.getImmanentNullability(); } private boolean putNullability(@NotNull Map map, @NotNull DataFlowValue value, @NotNull Nullability nullability) { if (!value.isStableIdentifier()) return false; - return map.put(value, nullability) != nullability; + map.put(value, nullability); + return nullability != getNullability(value); } @Override @NotNull public List getPossibleTypes(@NotNull DataFlowValue key) { - JetType originalType = key.getType(); List types = typeInfo.get(key); - Nullability nullability = getNullability(key); - if (nullability.canBeNull()) { + if (getNullability(key).canBeNull()) { + if (parent != null) { + types = Lists.newArrayList(types); + addAllUnique(types, parent.getPossibleTypes(key)); + } return types; } - List enrichedTypes = Lists.newArrayListWithCapacity(types.size()); + + List enrichedTypes = Lists.newArrayListWithCapacity(types.size() + 1); + JetType originalType = key.getType(); if (originalType.isNullable()) { enrichedTypes.add(TypeUtils.makeNotNullable(originalType)); } - for (JetType type: types) { - if (type.isNullable()) { - enrichedTypes.add(TypeUtils.makeNotNullable(type)); - } - else { - enrichedTypes.add(type); + for (JetType type : types) { + enrichedTypes.add(TypeUtils.makeNotNullable(type)); + } + + if (parent != null) { + addAllUnique(enrichedTypes, parent.getPossibleTypes(key)); + } + + return enrichedTypes; + } + + private static void addAllUnique(@NotNull List toList, @NotNull List fromList) { + for (JetType type : fromList) { + if (!toList.contains(type)) { + toList.add(type); } } - return enrichedTypes; } @Override @NotNull public DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { - Map builder = Maps.newHashMap(nullabilityInfo); + Map builder = Maps.newHashMap(); Nullability nullabilityOfA = getNullability(a); Nullability nullabilityOfB = getNullability(b); boolean changed = false; changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)); changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA)); - return changed ? new DataFlowInfoImpl(ImmutableMap.copyOf(builder), typeInfo) : this; + return changed ? new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) : this; } @Override @NotNull public DataFlowInfo disequate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { - Map builder = Maps.newHashMap(nullabilityInfo); + Map builder = Maps.newHashMap(); Nullability nullabilityOfA = getNullability(a); Nullability nullabilityOfB = getNullability(b); boolean changed = false; changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())); changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert())); - return changed ? new DataFlowInfoImpl(ImmutableMap.copyOf(builder), typeInfo) : this; + return changed ? new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) : this; } @Override @NotNull public DataFlowInfo establishSubtyping(@NotNull DataFlowValue[] values, @NotNull JetType type) { - ListMultimap newTypeInfo = copyTypeInfo(); - Map newNullabilityInfo = Maps.newHashMap(nullabilityInfo); - boolean changed = false; + if (values.length == 0) return this; + + ListMultimap newTypeInfo = newTypeInfo(); + Map newNullabilityInfo = Maps.newHashMap(); for (DataFlowValue value : values) { -// if (!value.isStableIdentifier()) continue; - changed = true; newTypeInfo.put(value, type); if (!type.isNullable()) { putNullability(newNullabilityInfo, value, NOT_NULL); } } - if (!changed) return this; - return new DataFlowInfoImpl(ImmutableMap.copyOf(newNullabilityInfo), newTypeInfo); + return new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(newNullabilityInfo), newTypeInfo); } + @NotNull private ListMultimap copyTypeInfo() { - ListMultimap newTypeInfo = Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); + ListMultimap newTypeInfo = newTypeInfo(); newTypeInfo.putAll(typeInfo); return newTypeInfo; } @@ -135,7 +157,11 @@ public class DataFlowInfoImpl implements DataFlowInfo { @NotNull @Override public DataFlowInfo and(@NotNull DataFlowInfo otherInfo) { - DataFlowInfoImpl other = (DataFlowInfoImpl) otherInfo; + if (otherInfo == EMPTY) return this; + + assert otherInfo instanceof DelegatingDataFlowInfo : "Unknown DataFlowInfo type: " + otherInfo; + DelegatingDataFlowInfo other = (DelegatingDataFlowInfo) otherInfo; + Map nullabilityMapBuilder = Maps.newHashMap(); nullabilityMapBuilder.putAll(nullabilityInfo); for (Map.Entry entry : other.nullabilityInfo.entrySet()) { @@ -152,13 +178,17 @@ public class DataFlowInfoImpl implements DataFlowInfo { ListMultimap newTypeInfo = copyTypeInfo(); newTypeInfo.putAll(other.typeInfo); - return new DataFlowInfoImpl(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo); + return new DelegatingDataFlowInfo(null, ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo); } @NotNull @Override public DataFlowInfo or(@NotNull DataFlowInfo otherInfo) { - DataFlowInfoImpl other = (DataFlowInfoImpl) otherInfo; + if (otherInfo == EMPTY) return EMPTY; + + assert otherInfo instanceof DelegatingDataFlowInfo : "Unknown DataFlowInfo type: " + otherInfo; + DelegatingDataFlowInfo other = (DelegatingDataFlowInfo) otherInfo; + Map builder = Maps.newHashMap(nullabilityInfo); builder.keySet().retainAll(other.nullabilityInfo.keySet()); for (Map.Entry entry : builder.entrySet()) { @@ -169,7 +199,7 @@ public class DataFlowInfoImpl implements DataFlowInfo { builder.put(key, thisFlags.or(otherFlags)); } - ListMultimap newTypeInfo = Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); + ListMultimap newTypeInfo = newTypeInfo(); Set keys = Sets.newHashSet(typeInfo.keySet()); keys.retainAll(other.typeInfo.keySet()); @@ -184,7 +214,12 @@ public class DataFlowInfoImpl implements DataFlowInfo { newTypeInfo.putAll(key, newTypes); } - return new DataFlowInfoImpl(ImmutableMap.copyOf(builder), newTypeInfo); + return new DelegatingDataFlowInfo(null, ImmutableMap.copyOf(builder), newTypeInfo); + } + + @NotNull + /* package */ static ListMultimap newTypeInfo() { + return Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); } @Override @@ -199,4 +234,4 @@ public class DataFlowInfoImpl implements DataFlowInfo { } return "Non-trivial DataFlowInfo"; } -} \ No newline at end of file +}