diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.kt index e8c09f0e7b9..6309f8c9f93 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.kt @@ -14,375 +14,284 @@ * limitations under the License. */ -package org.jetbrains.kotlin.resolve.calls.smartcasts; +package org.jetbrains.kotlin.resolve.calls.smartcasts -import com.google.common.collect.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.types.FlexibleTypesKt; -import org.jetbrains.kotlin.types.KotlinType; -import org.jetbrains.kotlin.types.TypeUtils; -import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; +import com.google.common.collect.* +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.typeUtil.* -import java.util.*; +import java.util.* -import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL; +import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL -/* package */ class DelegatingDataFlowInfo implements DataFlowInfo { - private static final ImmutableMap EMPTY_NULLABILITY_INFO = ImmutableMap.of(); - private static final SetMultimap EMPTY_TYPE_INFO = newTypeInfo(); +internal class DelegatingDataFlowInfo @JvmOverloads constructor( + private val parent: DataFlowInfo?, + private val nullabilityInfo: ImmutableMap, + // Also immutable + private val typeInfo: SetMultimap, + /** + * Value for which type info was cleared or reassigned at this point + * so parent type info should not be in use + */ + private val valueWithGivenTypeInfo: DataFlowValue? = null +) : DataFlowInfo { - @Nullable - private final DataFlowInfo parent; - - @NotNull - private final ImmutableMap nullabilityInfo; - - // Also immutable - @NotNull - private final SetMultimap typeInfo; - - /** - * Value for which type info was cleared or reassigned at this point - * so parent type info should not be in use - */ - @Nullable - private final DataFlowValue valueWithGivenTypeInfo; - - /* package */ DelegatingDataFlowInfo( - @Nullable DataFlowInfo parent, - @NotNull ImmutableMap nullabilityInfo, - @NotNull SetMultimap typeInfo - ) { - this(parent, nullabilityInfo, typeInfo, null); - } - - /* package */ DelegatingDataFlowInfo( - @Nullable DataFlowInfo parent, - @NotNull ImmutableMap nullabilityInfo, - @NotNull SetMultimap typeInfo, - @Nullable DataFlowValue valueWithGivenTypeInfo - ) { - this.parent = parent; - this.nullabilityInfo = nullabilityInfo; - this.typeInfo = typeInfo; - this.valueWithGivenTypeInfo = valueWithGivenTypeInfo; - } - - @Override - @NotNull - public Map getCompleteNullabilityInfo() { - Map result = Maps.newHashMap(); - DelegatingDataFlowInfo info = this; + override fun getCompleteNullabilityInfo(): Map { + val result = Maps.newHashMap() + var info: DelegatingDataFlowInfo? = this while (info != null) { - for (Map.Entry entry : info.nullabilityInfo.entrySet()) { - DataFlowValue key = entry.getKey(); - Nullability value = entry.getValue(); + for ((key, value) in info.nullabilityInfo) { if (!result.containsKey(key)) { - result.put(key, value); + result.put(key, value) } } - info = (DelegatingDataFlowInfo) info.parent; + info = info.parent as DelegatingDataFlowInfo? } - return result; + return result } - @Override - @NotNull - public SetMultimap getCompleteTypeInfo() { - SetMultimap result = newTypeInfo(); - Set withGivenTypeInfo = new HashSet(); - DelegatingDataFlowInfo info = this; + override fun getCompleteTypeInfo(): SetMultimap { + val result = newTypeInfo() + val withGivenTypeInfo = HashSet() + var info: DelegatingDataFlowInfo? = this while (info != null) { - for (DataFlowValue key : info.typeInfo.keySet()) { + for (key in info.typeInfo.keySet()) { if (!withGivenTypeInfo.contains(key)) { - result.putAll(key, info.typeInfo.get(key)); + result.putAll(key, info.typeInfo.get(key)) } } - if (info.valueWithGivenTypeInfo != null) { - withGivenTypeInfo.add(info.valueWithGivenTypeInfo); + info.valueWithGivenTypeInfo?.let { withGivenTypeInfo.add(it) } + info = info.parent as DelegatingDataFlowInfo? + } + return result + } + + override fun getCollectedNullability(key: DataFlowValue) = getNullability(key, false) + + override fun getPredictableNullability(key: DataFlowValue) = getNullability(key, true) + + private fun getNullability(key: DataFlowValue, predictableOnly: Boolean) = + if (predictableOnly && !key.isPredictable) { + key.immanentNullability } - info = (DelegatingDataFlowInfo) info.parent; - } - return result; + else { + nullabilityInfo[key] ?: if (parent != null) { + parent.getCollectedNullability(key) + } + else { + key.immanentNullability + } + } + + private fun putNullability(map: MutableMap, value: DataFlowValue, nullability: Nullability): Boolean { + map.put(value, nullability) + return nullability != getCollectedNullability(value) } - @Override - @NotNull - public Nullability getCollectedNullability(@NotNull DataFlowValue key) { - return getNullability(key, false); - } + override fun getCollectedTypes(key: DataFlowValue) = getCollectedTypes(key, true) - @Override - @NotNull - public Nullability getPredictableNullability(@NotNull DataFlowValue key) { - return getNullability(key, true); - } - - @NotNull - private Nullability getNullability(@NotNull DataFlowValue key, boolean predictableOnly) { - if (predictableOnly && !key.isPredictable()) return key.getImmanentNullability(); - Nullability nullability = nullabilityInfo.get(key); - return nullability != null ? nullability : - parent != null ? parent.getCollectedNullability(key) : - key.getImmanentNullability(); - } - - private boolean putNullability( - @NotNull Map map, - @NotNull DataFlowValue value, - @NotNull Nullability nullability - ) { - map.put(value, nullability); - return nullability != getCollectedNullability(value); - } - - @Override - @NotNull - public Set getCollectedTypes(@NotNull DataFlowValue key) { - return getCollectedTypes(key, true); - } - - @NotNull - private Set getCollectedTypes(@NotNull DataFlowValue key, boolean enrichWithNotNull) { - Set types = collectTypesFromMeAndParents(key); + private fun getCollectedTypes(key: DataFlowValue, enrichWithNotNull: Boolean): Set { + val types = collectTypesFromMeAndParents(key) if (!enrichWithNotNull || getCollectedNullability(key).canBeNull()) { - return types; + return types } - Set enrichedTypes = Sets.newHashSetWithExpectedSize(types.size() + 1); - KotlinType originalType = key.getType(); - if (originalType.isMarkedNullable()) { - enrichedTypes.add(TypeUtils.makeNotNullable(originalType)); + val enrichedTypes = Sets.newHashSetWithExpectedSize(types.size + 1) + val originalType = key.type + if (originalType.isMarkedNullable) { + enrichedTypes.add(TypeUtils.makeNotNullable(originalType)) } - for (KotlinType type : types) { - enrichedTypes.add(TypeUtils.makeNotNullable(type)); + for (type in types) { + enrichedTypes.add(TypeUtils.makeNotNullable(type)) } - return enrichedTypes; + return enrichedTypes } - @Override - @NotNull - public Set getPredictableTypes(@NotNull DataFlowValue key) { - return getPredictableTypes(key, true); - } + override fun getPredictableTypes(key: DataFlowValue) = getPredictableTypes(key, true) - @NotNull - private Set getPredictableTypes(@NotNull DataFlowValue key, boolean enrichWithNotNull) { - if (!key.isPredictable()) { - return new LinkedHashSet(); - } - return getCollectedTypes(key, enrichWithNotNull); - } + private fun getPredictableTypes(key: DataFlowValue, enrichWithNotNull: Boolean) = + if (!key.isPredictable) LinkedHashSet() else getCollectedTypes(key, enrichWithNotNull) /** * Call this function to clear all data flow information about * the given data flow value. - * + * @param value */ - @Override - @NotNull - public DataFlowInfo clearValueInfo(@NotNull DataFlowValue value) { - Map builder = Maps.newHashMap(); - putNullability(builder, value, Nullability.UNKNOWN); - return new DelegatingDataFlowInfo( - this, - ImmutableMap.copyOf(builder), - EMPTY_TYPE_INFO, - value - ); + override fun clearValueInfo(value: DataFlowValue): DataFlowInfo { + val builder = Maps.newHashMap() + putNullability(builder, value, Nullability.UNKNOWN) + return DelegatingDataFlowInfo( + this, + ImmutableMap.copyOf(builder), + EMPTY_TYPE_INFO, + value) } - @Override - @NotNull - public DataFlowInfo assign(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { - Map nullability = Maps.newHashMap(); - Nullability nullabilityOfB = getPredictableNullability(b); - putNullability(nullability, a, nullabilityOfB); + override fun assign(a: DataFlowValue, b: DataFlowValue): DataFlowInfo { + val nullability = Maps.newHashMap() + val nullabilityOfB = getPredictableNullability(b) + putNullability(nullability, a, nullabilityOfB) - SetMultimap newTypeInfo = newTypeInfo(); - Set typesForB = getPredictableTypes(b); + val newTypeInfo = newTypeInfo() + var 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 - if (nullabilityOfB.canBeNonNull() && !a.getType().equals(b.getType())) { - typesForB.add(b.getType()); + if (nullabilityOfB.canBeNonNull() && a.type != b.type) { + typesForB += b.type } - newTypeInfo.putAll(a, typesForB); + newTypeInfo.putAll(a, typesForB) - return new DelegatingDataFlowInfo( - this, - ImmutableMap.copyOf(nullability), - newTypeInfo.isEmpty() ? EMPTY_TYPE_INFO : newTypeInfo, - a - ); + return DelegatingDataFlowInfo( + this, + ImmutableMap.copyOf(nullability), + if (newTypeInfo.isEmpty) EMPTY_TYPE_INFO else newTypeInfo, + a) } - @Override - @NotNull - public DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { - Map builder = Maps.newHashMap(); - Nullability nullabilityOfA = getPredictableNullability(a); - Nullability nullabilityOfB = getPredictableNullability(b); + override fun equate(a: DataFlowValue, b: DataFlowValue): DataFlowInfo { + val builder = Maps.newHashMap() + val nullabilityOfA = getPredictableNullability(a) + val nullabilityOfB = getPredictableNullability(b) - boolean changed = false; - changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)); - changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA)); + var changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)) or + putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA)) - SetMultimap newTypeInfo = newTypeInfo(); - newTypeInfo.putAll(a, getPredictableTypes(b, false)); - newTypeInfo.putAll(b, getPredictableTypes(a, false)); - if (!a.getType().equals(b.getType())) { + val newTypeInfo = newTypeInfo() + newTypeInfo.putAll(a, getPredictableTypes(b, false)) + newTypeInfo.putAll(b, getPredictableTypes(a, false)) + if (a.type != b.type) { // To avoid recording base types of own type - if (!TypeUtilsKt.isSubtypeOf(a.getType(), b.getType())) { - newTypeInfo.put(a, b.getType()); + if (!a.type.isSubtypeOf(b.type)) { + newTypeInfo.put(a, b.type) } - if (!TypeUtilsKt.isSubtypeOf(b.getType(), a.getType())) { - newTypeInfo.put(b, a.getType()); + if (!b.type.isSubtypeOf(a.type)) { + newTypeInfo.put(b, a.type) } } - changed |= !newTypeInfo.isEmpty(); + changed = changed or !newTypeInfo.isEmpty - return !changed - ? this - : new DelegatingDataFlowInfo( - this, - ImmutableMap.copyOf(builder), - newTypeInfo.isEmpty() ? EMPTY_TYPE_INFO : newTypeInfo - ); + return if (!changed) { + this + } + else { + DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), if (newTypeInfo.isEmpty) EMPTY_TYPE_INFO else newTypeInfo) + } } - @NotNull - private Set collectTypesFromMeAndParents(@NotNull DataFlowValue value) { - Set types = new LinkedHashSet(); + private fun collectTypesFromMeAndParents(value: DataFlowValue): Set { + val types = LinkedHashSet() - DataFlowInfo current = this; + var current: DataFlowInfo? = this while (current != null) { - if (current instanceof DelegatingDataFlowInfo) { - DelegatingDataFlowInfo delegatingInfo = (DelegatingDataFlowInfo) current; - types.addAll(delegatingInfo.typeInfo.get(value)); - if (value.equals(delegatingInfo.valueWithGivenTypeInfo)) { - current = null; + if (current is DelegatingDataFlowInfo) { + types.addAll(current.typeInfo.get(value)) + if (value == current.valueWithGivenTypeInfo) { + current = null } else { - current = delegatingInfo.parent; + current = current.parent } } else { - types.addAll(current.getCollectedTypes(value)); - break; + types.addAll(current.getCollectedTypes(value)) + break } } - return types; + return types } - @Override - @NotNull - public DataFlowInfo disequate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { - Map builder = Maps.newHashMap(); - Nullability nullabilityOfA = getPredictableNullability(a); - Nullability nullabilityOfB = getPredictableNullability(b); + override fun disequate(a: DataFlowValue, b: DataFlowValue): DataFlowInfo { + val builder = Maps.newHashMap() + val nullabilityOfA = getPredictableNullability(a) + val nullabilityOfB = getPredictableNullability(b) - boolean changed = false; - changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())); - changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert())); - return changed ? new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) : this; + var changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())) or + putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert())) + return if (changed) DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) else this } - @Override - @NotNull - public DataFlowInfo establishSubtyping(@NotNull DataFlowValue value, @NotNull KotlinType type) { - if (value.getType().equals(type)) return this; - if (getCollectedTypes(value).contains(type)) return this; - if (!FlexibleTypesKt.isFlexible(value.getType()) && TypeUtilsKt.isSubtypeOf(value.getType(), type)) return this; - ImmutableMap newNullabilityInfo = - type.isMarkedNullable() ? EMPTY_NULLABILITY_INFO : ImmutableMap.of(value, NOT_NULL); - SetMultimap newTypeInfo = ImmutableSetMultimap.of(value, type); - return new DelegatingDataFlowInfo(this, newNullabilityInfo, newTypeInfo); + override fun establishSubtyping(value: DataFlowValue, type: KotlinType): DataFlowInfo { + if (value.type == type) return this + if (getCollectedTypes(value).contains(type)) return this + if (!value.type.isFlexible() && value.type.isSubtypeOf(type)) return this + val newNullabilityInfo = if (type.isMarkedNullable) EMPTY_NULLABILITY_INFO else ImmutableMap.of(value, NOT_NULL) + val newTypeInfo = ImmutableSetMultimap.of(value, type) + return DelegatingDataFlowInfo(this, newNullabilityInfo, newTypeInfo) } - @NotNull - @Override - public DataFlowInfo and(@NotNull DataFlowInfo otherInfo) { - if (otherInfo == EMPTY) return this; - if (this == EMPTY) return otherInfo; - if (this == otherInfo) return this; + override fun and(otherInfo: DataFlowInfo): DataFlowInfo { + if (otherInfo === DataFlowInfo.EMPTY) return this + if (this === DataFlowInfo.EMPTY) return otherInfo + if (this === otherInfo) return this - assert otherInfo instanceof DelegatingDataFlowInfo : "Unknown DataFlowInfo type: " + otherInfo; - DelegatingDataFlowInfo other = (DelegatingDataFlowInfo) otherInfo; + assert(otherInfo is DelegatingDataFlowInfo) { "Unknown DataFlowInfo type: " + otherInfo } + val other = otherInfo as DelegatingDataFlowInfo - Map nullabilityMapBuilder = Maps.newHashMap(); - for (Map.Entry entry : other.getCompleteNullabilityInfo().entrySet()) { - DataFlowValue key = entry.getKey(); - Nullability otherFlags = entry.getValue(); - Nullability thisFlags = getCollectedNullability(key); - Nullability flags = thisFlags.and(otherFlags); + val nullabilityMapBuilder = Maps.newHashMap() + for ((key, otherFlags) in other.completeNullabilityInfo) { + val thisFlags = getCollectedNullability(key) + val flags = thisFlags.and(otherFlags) if (flags != thisFlags) { - nullabilityMapBuilder.put(key, flags); + nullabilityMapBuilder.put(key, flags) } } - SetMultimap myTypeInfo = getCompleteTypeInfo(); - SetMultimap otherTypeInfo = other.getCompleteTypeInfo(); + val myTypeInfo = completeTypeInfo + val otherTypeInfo = other.completeTypeInfo if (nullabilityMapBuilder.isEmpty() && containsAll(myTypeInfo, otherTypeInfo)) { - return this; + return this } - return new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(nullabilityMapBuilder), otherTypeInfo); + return DelegatingDataFlowInfo(this, ImmutableMap.copyOf(nullabilityMapBuilder), otherTypeInfo) } - private static boolean containsAll(SetMultimap first, SetMultimap second) { - return first.entries().containsAll(second.entries()); + override fun or(otherInfo: DataFlowInfo): DataFlowInfo { + if (otherInfo === DataFlowInfo.EMPTY) return DataFlowInfo.EMPTY + if (this === DataFlowInfo.EMPTY) return DataFlowInfo.EMPTY + if (this === otherInfo) return this + + assert(otherInfo is DelegatingDataFlowInfo) { "Unknown DataFlowInfo type: " + otherInfo } + val other = otherInfo as DelegatingDataFlowInfo + + val nullabilityMapBuilder = Maps.newHashMap() + for ((key, otherFlags) in other.completeNullabilityInfo) { + val thisFlags = getCollectedNullability(key) + nullabilityMapBuilder.put(key, thisFlags.or(otherFlags)) + } + + val myTypeInfo = completeTypeInfo + val otherTypeInfo = other.completeTypeInfo + val newTypeInfo = newTypeInfo() + + for (key in Sets.intersection(myTypeInfo.keySet(), otherTypeInfo.keySet())) { + val thisTypes = myTypeInfo.get(key) + val otherTypes = otherTypeInfo.get(key) + newTypeInfo.putAll(key, Sets.intersection(thisTypes, otherTypes)) + } + + if (nullabilityMapBuilder.isEmpty() && newTypeInfo.isEmpty) { + return DataFlowInfo.EMPTY + } + + return DelegatingDataFlowInfo(null, ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo) } - @NotNull - @Override - public DataFlowInfo or(@NotNull DataFlowInfo otherInfo) { - if (otherInfo == EMPTY) return EMPTY; - if (this == EMPTY) return EMPTY; - if (this == otherInfo) return this; + override fun toString() = if (typeInfo.isEmpty && nullabilityInfo.isEmpty()) "EMPTY" else "Non-trivial DataFlowInfo" - assert otherInfo instanceof DelegatingDataFlowInfo : "Unknown DataFlowInfo type: " + otherInfo; - DelegatingDataFlowInfo other = (DelegatingDataFlowInfo) otherInfo; + companion object { + private val EMPTY_NULLABILITY_INFO = ImmutableMap.of() + private val EMPTY_TYPE_INFO = newTypeInfo() - Map nullabilityMapBuilder = Maps.newHashMap(); - for (Map.Entry entry : other.getCompleteNullabilityInfo().entrySet()) { - DataFlowValue key = entry.getKey(); - Nullability otherFlags = entry.getValue(); - Nullability thisFlags = getCollectedNullability(key); - nullabilityMapBuilder.put(key, thisFlags.or(otherFlags)); - } + private fun containsAll(first: SetMultimap, second: SetMultimap) = + first.entries().containsAll(second.entries()) - SetMultimap myTypeInfo = getCompleteTypeInfo(); - SetMultimap otherTypeInfo = other.getCompleteTypeInfo(); - SetMultimap newTypeInfo = newTypeInfo(); - - for (DataFlowValue key : Sets.intersection(myTypeInfo.keySet(), otherTypeInfo.keySet())) { - Set thisTypes = myTypeInfo.get(key); - Set otherTypes = otherTypeInfo.get(key); - newTypeInfo.putAll(key, Sets.intersection(thisTypes, otherTypes)); - } - - if (nullabilityMapBuilder.isEmpty() && newTypeInfo.isEmpty()) { - return EMPTY; - } - - return new DelegatingDataFlowInfo(null, ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo); - } - - @NotNull - /* package */ static SetMultimap newTypeInfo() { - return LinkedHashMultimap.create(); - } - - @Override - public String toString() { - if (typeInfo.isEmpty() && nullabilityInfo.isEmpty()) { - return "EMPTY"; - } - return "Non-trivial DataFlowInfo"; + @JvmStatic + fun newTypeInfo(): SetMultimap = LinkedHashMultimap.create() } }