Always set lower bound for dynamic types that are effectively final
This ensures that there is no way to create a dynamic type (upper=T, lower=null) if T is effectively final.
Change-Id: I8068541577a9a6ca49725edf78eafdb7aad73f9f
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 5db6f54..cae552a 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
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.analysis.type;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Objects;
@@ -19,6 +20,7 @@
public class DynamicType {
private static final DynamicType BOTTOM = new DynamicType(TypeElement.getBottom());
+ private static final DynamicType NULL_TYPE = new DynamicType(TypeElement.getNull());
private static final DynamicType UNKNOWN = new DynamicType(TypeElement.getTop());
private final TypeElement dynamicUpperBoundType;
@@ -29,12 +31,29 @@
}
public static DynamicType create(
+ AppView<AppInfoWithLiveness> appView, TypeElement dynamicUpperBoundType) {
+ ClassTypeElement dynamicLowerBoundType = null;
+ if (dynamicUpperBoundType.isClassType()) {
+ ClassTypeElement dynamicUpperBoundClassType = dynamicUpperBoundType.asClassType();
+ DexClass dynamicUpperBoundClass =
+ appView.definitionFor(dynamicUpperBoundClassType.getClassType());
+ if (dynamicUpperBoundClass != null && dynamicUpperBoundClass.isEffectivelyFinal(appView)) {
+ dynamicLowerBoundType = dynamicUpperBoundClassType;
+ }
+ }
+ return create(appView, dynamicUpperBoundType, dynamicLowerBoundType);
+ }
+
+ public static DynamicType create(
AppView<AppInfoWithLiveness> appView,
TypeElement dynamicUpperBoundType,
ClassTypeElement dynamicLowerBoundType) {
if (dynamicUpperBoundType.isBottom()) {
return bottom();
}
+ if (dynamicUpperBoundType.isNullType()) {
+ return definitelyNull();
+ }
if (dynamicUpperBoundType.isTop()) {
return unknown();
}
@@ -47,6 +66,7 @@
return DynamicTypeWithLowerBound.create(
appView, dynamicUpperBoundType.asClassType(), dynamicLowerBoundType);
}
+ assert verifyNotEffectivelyFinalClassType(appView, dynamicUpperBoundType);
return new DynamicType(dynamicUpperBoundType);
}
@@ -54,11 +74,12 @@
return new ExactDynamicType(exactDynamicType);
}
- public static DynamicType create(Value value, AppView<AppInfoWithLiveness> appView) {
+ public static DynamicType create(AppView<AppInfoWithLiveness> appView, Value value) {
assert value.getType().isReferenceType();
TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
ClassTypeElement dynamicLowerBoundType =
- value.getDynamicLowerBoundType(appView, dynamicUpperBoundType.nullability());
+ value.getDynamicLowerBoundType(
+ appView, dynamicUpperBoundType, dynamicUpperBoundType.nullability());
return create(appView, dynamicUpperBoundType, dynamicLowerBoundType);
}
@@ -66,6 +87,10 @@
return BOTTOM;
}
+ public static DynamicType definitelyNull() {
+ return NULL_TYPE;
+ }
+
public static DynamicType unknown() {
return UNKNOWN;
}
@@ -86,6 +111,10 @@
return getDynamicUpperBoundType().isBottom();
}
+ public boolean isNullType() {
+ return getDynamicUpperBoundType().isNullType();
+ }
+
public boolean isTrivial(TypeElement staticType) {
return staticType.equals(getDynamicUpperBoundType()) || isUnknown();
}
@@ -116,6 +145,18 @@
private ClassTypeElement meetDynamicLowerBound(
AppView<AppInfoWithLiveness> appView, DynamicType dynamicType) {
+ if (isNullType()) {
+ if (dynamicType.hasDynamicLowerBoundType()) {
+ return dynamicType.getDynamicLowerBoundType().joinNullability(Nullability.definitelyNull());
+ }
+ return null;
+ }
+ if (dynamicType.isNullType()) {
+ if (hasDynamicLowerBoundType()) {
+ return getDynamicLowerBoundType().joinNullability(Nullability.definitelyNull());
+ }
+ return null;
+ }
if (!hasDynamicLowerBoundType() || !dynamicType.hasDynamicLowerBoundType()) {
return null;
}
@@ -143,4 +184,27 @@
public int hashCode() {
return dynamicUpperBoundType.hashCode();
}
+
+ private static boolean verifyNotEffectivelyFinalClassType(
+ AppView<AppInfoWithLiveness> appView, TypeElement type) {
+ if (type.isClassType()) {
+ ClassTypeElement classType = type.asClassType();
+ DexClass clazz = appView.definitionFor(classType.getClassType());
+ assert clazz == null || !clazz.isEffectivelyFinal(appView);
+ }
+ return true;
+ }
+
+ public DynamicType withNullability(Nullability nullability) {
+ assert !hasDynamicLowerBoundType();
+ if (!getDynamicUpperBoundType().isReferenceType()) {
+ return this;
+ }
+ ReferenceTypeElement dynamicUpperBoundReferenceType =
+ getDynamicUpperBoundType().asReferenceType();
+ if (dynamicUpperBoundReferenceType.nullability() == nullability) {
+ return this;
+ }
+ return new DynamicType(dynamicUpperBoundReferenceType.getOrCreateVariant(nullability));
+ }
}
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 400f99e..0983de7 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
@@ -12,7 +12,7 @@
private final ClassTypeElement dynamicLowerBoundType;
- DynamicTypeWithLowerBound(
+ private DynamicTypeWithLowerBound(
ClassTypeElement dynamicUpperBoundType, ClassTypeElement dynamicLowerBoundType) {
super(dynamicUpperBoundType);
assert !dynamicUpperBoundType.equals(dynamicLowerBoundType);
@@ -34,6 +34,11 @@
}
@Override
+ public ClassTypeElement getDynamicUpperBoundType() {
+ return super.getDynamicUpperBoundType().asClassType();
+ }
+
+ @Override
public boolean hasDynamicLowerBoundType() {
return true;
}
@@ -62,4 +67,14 @@
public int hashCode() {
return Objects.hash(getDynamicUpperBoundType(), getDynamicLowerBoundType());
}
+
+ @Override
+ public DynamicType withNullability(Nullability nullability) {
+ if (getDynamicUpperBoundType().nullability() == nullability) {
+ return this;
+ }
+ return new DynamicTypeWithLowerBound(
+ getDynamicUpperBoundType().getOrCreateVariant(nullability),
+ getDynamicLowerBoundType().getOrCreateVariant(nullability));
+ }
}
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
index a6061d9..ed9f300 100644
--- 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
@@ -16,8 +16,13 @@
}
@Override
+ public ClassTypeElement getDynamicUpperBoundType() {
+ return super.getDynamicUpperBoundType().asClassType();
+ }
+
+ @Override
public ClassTypeElement getDynamicLowerBoundType() {
- return getDynamicUpperBoundType().asClassType();
+ return getDynamicUpperBoundType();
}
@Override
@@ -38,4 +43,12 @@
public int hashCode() {
return getDynamicLowerBoundType().hashCode();
}
+
+ @Override
+ public DynamicType withNullability(Nullability nullability) {
+ if (getDynamicUpperBoundType().nullability() == nullability) {
+ return this;
+ }
+ return new ExactDynamicType(getDynamicUpperBoundType().getOrCreateVariant(nullability));
+ }
}
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 4df7f91..0c58032 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
@@ -1086,7 +1086,7 @@
}
public DynamicType getDynamicType(AppView<AppInfoWithLiveness> appView) {
- return DynamicType.create(this, appView);
+ return DynamicType.create(appView, this);
}
public TypeElement getDynamicUpperBoundType(
@@ -1137,18 +1137,23 @@
}
public ClassTypeElement getDynamicLowerBoundType(AppView<AppInfoWithLiveness> appView) {
- return getDynamicLowerBoundType(appView, Nullability.maybeNull());
+ return getDynamicLowerBoundType(appView, null, 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.meetNullability(maxNullability);
+ AppView<AppInfoWithLiveness> appView,
+ TypeElement dynamicUpperBoundType,
+ Nullability maxNullability) {
+ // If the dynamic upper bound type is a final or effectively-final class type, then we know the
+ // lower bound. Since the dynamic upper bound type is below the static type in the class
+ // hierarchy, we only need to check if the dynamic upper bound type is effectively final if it
+ // is present.
+ TypeElement upperBoundType = dynamicUpperBoundType != null ? dynamicUpperBoundType : getType();
+ if (upperBoundType.isClassType()) {
+ ClassTypeElement upperBoundClassType = upperBoundType.asClassType();
+ DexClass upperBoundClass = appView.definitionFor(upperBoundClassType.getClassType());
+ if (upperBoundClass != null && upperBoundClass.isEffectivelyFinal(appView)) {
+ return upperBoundClassType.meetNullability(maxNullability);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index 9847171..1ef0397 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -234,9 +234,6 @@
// and should therefore not be too expensive to compute). Doing so should have the same
// precision, but lead to less state propagation in the subsequent top-down class
// hierarchy traversals.
- // TODO(b/190154391): Normalize type bounds that are the same, by removing trivial lower bound
- // information. For example, the types (upper=B, lower=unknown) and (upper=B, lower=B) are
- // identical if B does not have any subtypes.
DynamicType bounds =
invoke.isInvokeSuper()
? DynamicType.createExact(