Add ExactDynamicType and account for incomplete type canonicalization
Bug: 197237400
Change-Id: Iaa5981ac2a00a8fbfebfde6d8a747e0c0665e7d1
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index c171e7d..9e83183 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -145,6 +145,11 @@
}
@Override
+ public ClassTypeElement meetNullability(Nullability nullability) {
+ return getOrCreateVariant(nullability().meet(nullability));
+ }
+
+ @Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(nullability);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
index 12d1c4c..5db6f54 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Objects;
/**
* Represents the runtime type of a reference value. This type may be more precise than the value's
@@ -27,18 +28,40 @@
this.dynamicUpperBoundType = dynamicUpperBoundType;
}
- public static DynamicType create(Value value, AppView<AppInfoWithLiveness> appView) {
- assert value.getType().isReferenceType();
- TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
- ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
+ public static DynamicType create(
+ AppView<AppInfoWithLiveness> appView,
+ TypeElement dynamicUpperBoundType,
+ ClassTypeElement dynamicLowerBoundType) {
+ if (dynamicUpperBoundType.isBottom()) {
+ return bottom();
+ }
+ if (dynamicUpperBoundType.isTop()) {
+ return unknown();
+ }
if (dynamicLowerBoundType != null) {
assert dynamicUpperBoundType.isClassType();
+ assert dynamicUpperBoundType.nullability() == dynamicLowerBoundType.nullability();
+ if (dynamicUpperBoundType.equals(dynamicLowerBoundType)) {
+ return createExact(dynamicLowerBoundType);
+ }
return DynamicTypeWithLowerBound.create(
appView, dynamicUpperBoundType.asClassType(), dynamicLowerBoundType);
}
return new DynamicType(dynamicUpperBoundType);
}
+ public static DynamicType createExact(ClassTypeElement exactDynamicType) {
+ return new ExactDynamicType(exactDynamicType);
+ }
+
+ public static DynamicType create(Value value, AppView<AppInfoWithLiveness> appView) {
+ assert value.getType().isReferenceType();
+ TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
+ ClassTypeElement dynamicLowerBoundType =
+ value.getDynamicLowerBoundType(appView, dynamicUpperBoundType.nullability());
+ return create(appView, dynamicUpperBoundType, dynamicLowerBoundType);
+ }
+
public static DynamicType bottom() {
return BOTTOM;
}
@@ -59,21 +82,61 @@
return null;
}
+ public boolean isBottom() {
+ return getDynamicUpperBoundType().isBottom();
+ }
+
public boolean isTrivial(TypeElement staticType) {
- return staticType == getDynamicUpperBoundType() || isUnknown();
+ return staticType.equals(getDynamicUpperBoundType()) || isUnknown();
}
public boolean isUnknown() {
return getDynamicUpperBoundType().isTop();
}
+ public DynamicType join(AppView<AppInfoWithLiveness> appView, DynamicType dynamicType) {
+ if (isBottom()) {
+ return dynamicType;
+ }
+ if (dynamicType.isBottom() || equals(dynamicType)) {
+ return this;
+ }
+ if (isUnknown() || dynamicType.isUnknown()) {
+ return unknown();
+ }
+ TypeElement upperBoundType =
+ getDynamicUpperBoundType().join(dynamicType.getDynamicUpperBoundType(), appView);
+ ClassTypeElement lowerBoundType = meetDynamicLowerBound(appView, dynamicType);
+ if (upperBoundType.equals(getDynamicUpperBoundType())
+ && Objects.equals(lowerBoundType, getDynamicLowerBoundType())) {
+ return this;
+ }
+ return create(appView, upperBoundType, lowerBoundType);
+ }
+
+ private ClassTypeElement meetDynamicLowerBound(
+ AppView<AppInfoWithLiveness> appView, DynamicType dynamicType) {
+ if (!hasDynamicLowerBoundType() || !dynamicType.hasDynamicLowerBoundType()) {
+ return null;
+ }
+ ClassTypeElement lowerBoundType = getDynamicLowerBoundType();
+ ClassTypeElement otherLowerBoundType = dynamicType.getDynamicLowerBoundType();
+ if (lowerBoundType.lessThanOrEqualUpToNullability(otherLowerBoundType, appView)) {
+ return lowerBoundType.joinNullability(otherLowerBoundType.nullability());
+ }
+ if (otherLowerBoundType.lessThanOrEqualUpToNullability(lowerBoundType, appView)) {
+ return otherLowerBoundType.joinNullability(lowerBoundType.nullability());
+ }
+ return null;
+ }
+
@Override
public boolean equals(Object other) {
if (other == null || getClass() != other.getClass()) {
return false;
}
- DynamicType assumption = (DynamicType) other;
- return dynamicUpperBoundType == assumption.dynamicUpperBoundType;
+ DynamicType dynamicType = (DynamicType) other;
+ return dynamicUpperBoundType.equals(dynamicType.dynamicUpperBoundType);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
index ae61ee3..400f99e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
@@ -15,16 +15,21 @@
DynamicTypeWithLowerBound(
ClassTypeElement dynamicUpperBoundType, ClassTypeElement dynamicLowerBoundType) {
super(dynamicUpperBoundType);
+ assert !dynamicUpperBoundType.equals(dynamicLowerBoundType);
this.dynamicLowerBoundType = dynamicLowerBoundType;
}
- public static DynamicTypeWithLowerBound create(
+ static DynamicTypeWithLowerBound create(
AppView<AppInfoWithLiveness> appView,
ClassTypeElement dynamicUpperBoundType,
ClassTypeElement dynamicLowerBoundType) {
+ assert dynamicUpperBoundType != null;
+ assert dynamicLowerBoundType != null;
+ assert dynamicUpperBoundType.nullability() == dynamicLowerBoundType.nullability();
assert appView
.appInfo()
- .isSubtype(dynamicLowerBoundType.getClassType(), dynamicUpperBoundType.getClassType());
+ .isStrictSubtypeOf(
+ dynamicLowerBoundType.getClassType(), dynamicUpperBoundType.getClassType());
return new DynamicTypeWithLowerBound(dynamicUpperBoundType, dynamicLowerBoundType);
}
@@ -48,9 +53,9 @@
if (other == null || getClass() != other.getClass()) {
return false;
}
- DynamicTypeWithLowerBound assumption = (DynamicTypeWithLowerBound) other;
- return getDynamicUpperBoundType() == assumption.getDynamicUpperBoundType()
- && getDynamicLowerBoundType() == assumption.getDynamicLowerBoundType();
+ DynamicTypeWithLowerBound dynamicType = (DynamicTypeWithLowerBound) other;
+ return getDynamicUpperBoundType().equals(dynamicType.getDynamicUpperBoundType())
+ && getDynamicLowerBoundType().equals(dynamicType.getDynamicLowerBoundType());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
new file mode 100644
index 0000000..a6061d9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.type;
+
+public class ExactDynamicType extends DynamicType {
+
+ ExactDynamicType(ClassTypeElement exactDynamicType) {
+ super(exactDynamicType);
+ }
+
+ @Override
+ public boolean hasDynamicLowerBoundType() {
+ return true;
+ }
+
+ @Override
+ public ClassTypeElement getDynamicLowerBoundType() {
+ return getDynamicUpperBoundType().asClassType();
+ }
+
+ @Override
+ public boolean isTrivial(TypeElement staticType) {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ ExactDynamicType dynamicType = (ExactDynamicType) other;
+ return getDynamicUpperBoundType().equals(dynamicType.getDynamicUpperBoundType());
+ }
+
+ @Override
+ public int hashCode() {
+ return getDynamicLowerBoundType().hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
index 5a82ac4..3577c72 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
@@ -94,6 +94,10 @@
return getOrCreateVariant(nullability().join(nullability));
}
+ public ReferenceTypeElement meetNullability(Nullability nullability) {
+ return getOrCreateVariant(nullability().meet(nullability));
+ }
+
@Override
public boolean isReferenceType() {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 643fdcb..4df7f91 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -1136,13 +1137,18 @@
}
public ClassTypeElement getDynamicLowerBoundType(AppView<AppInfoWithLiveness> appView) {
+ return getDynamicLowerBoundType(appView, Nullability.maybeNull());
+ }
+
+ public ClassTypeElement getDynamicLowerBoundType(
+ AppView<AppInfoWithLiveness> appView, Nullability maxNullability) {
// If it is a final or effectively-final class type, then we know the lower bound.
if (getType().isClassType()) {
ClassTypeElement classType = getType().asClassType();
DexType type = classType.getClassType();
DexClass clazz = appView.definitionFor(type);
if (clazz != null && clazz.isEffectivelyFinal(appView)) {
- return classType;
+ return classType.meetNullability(maxNullability);
}
}
@@ -1151,11 +1157,12 @@
return null;
}
- Instruction definition = root.definition;
+ Instruction definition = root.getDefinition();
if (definition.isNewInstance()) {
- DexType type = definition.asNewInstance().clazz;
+ DexType type = definition.asNewInstance().getType();
DexClass clazz = appView.definitionFor(type);
if (clazz != null && !clazz.isInterface()) {
+ assert !maxNullability.isBottom();
return TypeElement.fromDexType(type, definitelyNotNull(), appView).asClassType();
}
return null;
@@ -1163,13 +1170,19 @@
// Try to find an alias of the receiver, which is defined by an instruction of the type Assume.
Value aliasedValue =
- getSpecificAliasedValue(value -> value.definition.isAssumeWithDynamicTypeAssumption());
+ getSpecificAliasedValue(value -> value.getDefinition().isAssumeWithDynamicTypeAssumption());
if (aliasedValue != null) {
- ClassTypeElement lattice =
- aliasedValue.definition.asAssume().getDynamicTypeAssumption().getDynamicLowerBoundType();
- return lattice != null && type.isDefinitelyNotNull() && lattice.isNullable()
- ? lattice.asMeetWithNotNull()
- : lattice;
+ ClassTypeElement aliasedValueType =
+ aliasedValue
+ .getDefinition()
+ .asAssume()
+ .getDynamicTypeAssumption()
+ .getDynamicLowerBoundType();
+ if (aliasedValueType != null) {
+ aliasedValueType = aliasedValueType.meetNullability(getType().nullability());
+ assert aliasedValueType.nullability().lessThanOrEqual(maxNullability);
+ return aliasedValueType;
+ }
}
return null;