Model array types as subtypes of Cloneable and Serializable
Bug: 214496607
Change-Id: I9dd814a312a3813abfa99ebae25f064a16c8ad0e
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
index 78332d5..9b5ea8e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
@@ -165,6 +165,22 @@
}
}
+ ReferenceTypeElement join(ClassTypeElement other, AppView<?> appView) {
+ return other.join(this, appView);
+ }
+
+ @Override
+ public ReferenceTypeElement join(ReferenceTypeElement other, AppView<?> appView) {
+ if (other.isArrayType()) {
+ return join(other.asArrayType(), appView);
+ }
+ if (other.isClassType()) {
+ return join(other.asClassType(), appView);
+ }
+ assert other.isNullType();
+ return joinNullability(other.nullability());
+ }
+
private static ReferenceTypeElement joinMember(
TypeElement aMember, TypeElement bMember, AppView<?> appView, Nullability nullability) {
if (aMember.equals(bMember)) {
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 7e54571..5ff246a 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
@@ -259,18 +259,39 @@
}
ClassTypeElement join(ClassTypeElement other, AppView<?> appView) {
- if (!appView.enableWholeProgramOptimizations()) {
- assert lazyInterfaces != null;
- assert lazyInterfaces.isEmpty();
- assert other.lazyInterfaces != null;
- assert other.lazyInterfaces.isEmpty();
- return ClassTypeElement.createForD8(
- getClassType() == other.getClassType()
- ? getClassType()
- : appView.dexItemFactory().objectType,
- nullability().join(other.nullability()));
+ if (appView.enableWholeProgramOptimizations()) {
+ return joinWithClassHierarchy(other);
+ } else {
+ return joinWithoutClassHierarchy(other.getClassType(), other.nullability(), appView);
}
- return joinWithClassHierarchy(other);
+ }
+
+ ReferenceTypeElement join(ArrayTypeElement other, AppView<?> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ if (appView.enableWholeProgramOptimizations()) {
+ DexType lubType = appView.dexItemFactory().objectType;
+ return joinWithClassHierarchy(
+ lubType,
+ InterfaceCollection.builder()
+ .addKnownInterface(dexItemFactory.cloneableType)
+ .addKnownInterface(dexItemFactory.serializableType)
+ .build(),
+ other.nullability());
+ } else {
+ return joinWithoutClassHierarchy(dexItemFactory.objectType, other.nullability(), appView);
+ }
+ }
+
+ @Override
+ public ReferenceTypeElement join(ReferenceTypeElement other, AppView<?> appView) {
+ if (other.isArrayType()) {
+ return join(other.asArrayType(), appView);
+ }
+ if (other.isClassType()) {
+ return join(other.asClassType(), appView);
+ }
+ assert other.isNullType();
+ return joinNullability(other.nullability());
}
private ClassTypeElement joinWithClassHierarchy(ClassTypeElement other) {
@@ -278,17 +299,23 @@
assert appView.enableWholeProgramOptimizations();
DexType lubType =
computeLeastUpperBoundOfClasses(appView.appInfo(), getClassType(), other.getClassType());
- InterfaceCollection c1lubItfs = getInterfaces();
- InterfaceCollection c2lubItfs = other.getInterfaces();
- InterfaceCollection lubItfs =
- c1lubItfs.equals(c2lubItfs)
- ? c1lubItfs
- : computeLeastUpperBoundOfInterfaces(appView, c1lubItfs, c2lubItfs);
- InterfaceCollection lubItfsDefault =
+ return joinWithClassHierarchy(lubType, other.getInterfaces(), other.nullability());
+ }
+
+ private ClassTypeElement joinWithClassHierarchy(
+ DexType lubType, InterfaceCollection otherInterfaces, Nullability nullability) {
+ assert appView != null;
+ assert appView.enableWholeProgramOptimizations();
+ InterfaceCollection interfaces = getInterfaces();
+ InterfaceCollection lubInterfaces =
+ interfaces.equals(otherInterfaces)
+ ? interfaces
+ : computeLeastUpperBoundOfInterfaces(appView, interfaces, otherInterfaces);
+ InterfaceCollection lubInterfacesDefault =
appView
.dexItemFactory()
.getOrComputeLeastUpperBoundOfImplementedInterfaces(lubType, appView);
- Nullability lubNullability = nullability().join(other.nullability());
+ Nullability lubNullability = nullability().join(nullability);
// If the computed interfaces are identical to the interfaces of `lubType`, then do not include
// the interfaces in the ClassTypeElement. This canonicalization of interfaces reduces memory,
@@ -296,9 +323,19 @@
// element does not require any rewriting).
//
// From a correctness point of view, both solutions should work.
- return lubItfs.equals(lubItfsDefault)
+ return lubInterfaces.equals(lubInterfacesDefault)
? create(lubType, lubNullability, appView)
- : create(lubType, lubNullability, appView, lubItfs);
+ : create(lubType, lubNullability, appView, lubInterfaces);
+ }
+
+ ClassTypeElement joinWithoutClassHierarchy(
+ DexType other, Nullability nullability, AppView<?> appView) {
+ assert !appView.enableWholeProgramOptimizations();
+ assert lazyInterfaces != null;
+ assert lazyInterfaces.isEmpty();
+ return createForD8(
+ getClassType() == other ? getClassType() : appView.dexItemFactory().objectType,
+ nullability().join(nullability));
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java b/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
index 02c35fd..7f4e28a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
@@ -56,6 +56,10 @@
return this;
}
+ public Builder addKnownInterface(DexType type) {
+ return addInterface(type, true);
+ }
+
public InterfaceCollection build() {
if (interfaces.isEmpty()) {
return InterfaceCollection.empty();
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 3577c72..ed144be 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.type;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
public abstract class ReferenceTypeElement extends TypeElement {
@@ -33,6 +34,11 @@
}
@Override
+ public ReferenceTypeElement join(ReferenceTypeElement other, AppView<?> appView) {
+ return other.joinNullability(nullability());
+ }
+
+ @Override
public String toString() {
return nullability.toString() + " " + DexItemFactory.nullValueType.toString();
}
@@ -90,6 +96,8 @@
return getOrCreateVariant(Nullability.maybeNull());
}
+ public abstract ReferenceTypeElement join(ReferenceTypeElement other, AppView<?> appView);
+
public ReferenceTypeElement joinNullability(Nullability nullability) {
return getOrCreateVariant(nullability().join(nullability));
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index 6451a1c..ef0e76e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -5,7 +5,6 @@
import static java.util.Collections.emptySet;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
@@ -119,48 +118,24 @@
* @return {@link TypeElement}, a least upper bound of {@param this} and {@param other}.
*/
public TypeElement join(TypeElement other, AppView<?> appView) {
- if (this == other) {
+ if (this == other || other.isBottom()) {
return this;
}
if (isBottom()) {
return other;
}
- if (other.isBottom()) {
- return this;
- }
- if (isTop() || other.isTop()) {
+ if (isTop() || other.isTop() || isPrimitiveType() != other.isPrimitiveType()) {
return getTop();
}
if (isPrimitiveType()) {
- return other.isPrimitiveType() ? asPrimitiveType().join(other.asPrimitiveType()) : getTop();
- }
- if (other.isPrimitiveType()) {
- // By the above case, !(isPrimitive())
- return getTop();
+ return asPrimitiveType().join(other.asPrimitiveType());
}
// From now on, this and other are precise reference types, i.e., either ArrayType or ClassType.
- assert isReferenceType() && other.isReferenceType();
- assert isPreciseType() && other.isPreciseType();
- Nullability nullabilityJoin = nullability().join(other.nullability());
- if (isNullType()) {
- return other.asReferenceType().getOrCreateVariant(nullabilityJoin);
- }
- if (other.isNullType()) {
- return this.asReferenceType().getOrCreateVariant(nullabilityJoin);
- }
- if (getClass() != other.getClass()) {
- return objectClassType(appView, nullabilityJoin);
- }
- // From now on, getClass() == other.getClass()
- if (isArrayType()) {
- assert other.isArrayType();
- return asArrayType().join(other.asArrayType(), appView);
- }
- if (isClassType()) {
- assert other.isClassType();
- return asClassType().join(other.asClassType(), appView);
- }
- throw new Unreachable("unless a new type lattice is introduced.");
+ assert isReferenceType();
+ assert isPreciseType();
+ assert other.isReferenceType();
+ assert other.isPreciseType();
+ return asReferenceType().join(other.asReferenceType(), appView);
}
public static TypeElement join(Iterable<TypeElement> typeLattices, AppView<?> appView) {
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
index c9d943e..4ad8b97 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
@@ -65,9 +65,6 @@
builder ->
builder.addOptionsModification(
options -> {
- options
- .getOpenClosedInterfacesOptions()
- .suppressArrayAssignmentsToJavaLangSerializable();
options.testing.processingContextsConsumer =
id -> assertNull(idsRoundOne.put(id, id));
}));
@@ -80,9 +77,6 @@
builder ->
builder.addOptionsModification(
options -> {
- options
- .getOpenClosedInterfacesOptions()
- .suppressArrayAssignmentsToJavaLangSerializable();
options.testing.processingContextsConsumer =
id -> {
AssertionUtils.assertNotNull(idsRoundOne.get(id));
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
index 34b77bd..a6277d6 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
@@ -63,9 +63,6 @@
builder ->
builder.addOptionsModification(
options -> {
- options
- .getOpenClosedInterfacesOptions()
- .suppressArrayAssignmentsToJavaLangSerializable();
options.testing.processingContextsConsumer =
id -> assertTrue(idsRoundOne.add(id));
}));
@@ -78,9 +75,6 @@
builder ->
builder.addOptionsModification(
options -> {
- options
- .getOpenClosedInterfacesOptions()
- .suppressArrayAssignmentsToJavaLangSerializable();
options.testing.processingContextsConsumer =
id -> assertTrue(idsRoundTwo.add(id));
}));
@@ -104,9 +98,6 @@
builder.addOptionsModification(
options -> {
options.testing.forceJumboStringProcessing = true;
- options
- .getOpenClosedInterfacesOptions()
- .suppressArrayAssignmentsToJavaLangSerializable();
}))
.runDex2Oat(parameters.getRuntime())
.assertNoVerificationErrors();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ArrayInstanceOfCloneableAndSerializableTest.java b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ArrayInstanceOfCloneableAndSerializableTest.java
index 73f3cfe..fc8ca51 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ArrayInstanceOfCloneableAndSerializableTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ArrayInstanceOfCloneableAndSerializableTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.instanceofremoval;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
@@ -71,12 +72,10 @@
.inspect(
inspector -> {
MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
- assertEquals(
- 2,
+ assertTrue(
mainMethodSubject
.streamInstructions()
- .filter(InstructionSubject::isInstanceOf)
- .count());
+ .noneMatch(InstructionSubject::isInstanceOf));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("true", "true");