Check for instantiated class when passing in lower bound
Change-Id: Iacf1c9c71f43b918d84c7936129a814d82c673cd
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index f9cea40..5c1611d 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -410,14 +410,11 @@
// TODO(b/148769279): Remove the check for hasInstantiatedLambdas.
Box<Boolean> hasInstantiatedLambdas = new Box<>(false);
InstantiatedSubTypeInfo instantiatedSubTypeInfo =
- refinedReceiverLowerBound == null
- ? instantiatedSubTypeInfoWithoutLowerBound(
- appInfo, refinedReceiverUpperBound, hasInstantiatedLambdas)
- : instantiatedSubTypeInfoWithLowerBound(
- appInfo,
- refinedReceiverUpperBound,
- refinedReceiverLowerBound,
- hasInstantiatedLambdas);
+ instantiatedSubTypeInfoForInstantiatedType(
+ appInfo,
+ refinedReceiverUpperBound,
+ refinedReceiverLowerBound,
+ hasInstantiatedLambdas);
LookupResult lookupResult =
lookupVirtualDispatchTargets(
context,
@@ -430,37 +427,28 @@
return lookupResult;
}
- private InstantiatedSubTypeInfo instantiatedSubTypeInfoWithoutLowerBound(
- AppInfoWithLiveness appInfo,
- DexProgramClass refinedReceiverUpperBound,
- Box<Boolean> hasInstantiatedLambdas) {
- return (type, subTypeConsumer, callSiteConsumer) -> {
- appInfo.forEachInstantiatedSubType(
- refinedReceiverUpperBound.type,
- subType -> {
- if (appInfo.hasAnyInstantiatedLambdas(subType)) {
- hasInstantiatedLambdas.set(true);
- }
- subTypeConsumer.accept(subType);
- },
- callSiteConsumer);
- };
- }
-
- private InstantiatedSubTypeInfo instantiatedSubTypeInfoWithLowerBound(
+ private InstantiatedSubTypeInfo instantiatedSubTypeInfoForInstantiatedType(
AppInfoWithLiveness appInfo,
DexProgramClass refinedReceiverUpperBound,
DexProgramClass refinedReceiverLowerBound,
Box<Boolean> hasInstantiatedLambdas) {
- return (type, subTypeConsumer, callSiteConsumer) -> {
- List<DexProgramClass> subTypes =
- appInfo.computeProgramClassRelationChain(
- refinedReceiverLowerBound, refinedReceiverUpperBound);
- for (DexProgramClass subType : subTypes) {
- if (appInfo.hasAnyInstantiatedLambdas(subType)) {
- hasInstantiatedLambdas.set(true);
- }
- subTypeConsumer.accept(subType);
+ return (ignored, subTypeConsumer, callSiteConsumer) -> {
+ Consumer<DexProgramClass> lambdaInstantiatedConsumer =
+ subType -> {
+ subTypeConsumer.accept(subType);
+ if (appInfo.hasAnyInstantiatedLambdas(subType)) {
+ hasInstantiatedLambdas.set(true);
+ }
+ };
+ if (refinedReceiverLowerBound == null) {
+ appInfo.forEachInstantiatedSubType(
+ refinedReceiverUpperBound.type, lambdaInstantiatedConsumer, callSiteConsumer);
+ } else {
+ appInfo.forEachInstantiatedSubTypeInChain(
+ refinedReceiverUpperBound,
+ refinedReceiverLowerBound,
+ lambdaInstantiatedConsumer,
+ callSiteConsumer);
}
};
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 4ec2517..eef80a6 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1364,14 +1364,32 @@
if (clazz == null) {
continue;
}
- if (isInstantiatedDirectly(clazz)
- || isPinned(clazz.type)
- || hasAnyInstantiatedLambdas(clazz)) {
+ if (isInstantiatedOrPinned(clazz)) {
subTypeConsumer.accept(clazz);
}
}
}
+ public void forEachInstantiatedSubTypeInChain(
+ DexProgramClass refinedReceiverUpperBound,
+ DexProgramClass refinedReceiverLowerBound,
+ Consumer<DexProgramClass> subTypeConsumer,
+ Consumer<LambdaDescriptor> callSiteConsumer) {
+ List<DexProgramClass> subTypes =
+ computeProgramClassRelationChain(refinedReceiverLowerBound, refinedReceiverUpperBound);
+ for (DexProgramClass subType : subTypes) {
+ if (isInstantiatedOrPinned(subType)) {
+ subTypeConsumer.accept(subType);
+ }
+ }
+ }
+
+ private boolean isInstantiatedOrPinned(DexProgramClass clazz) {
+ return isInstantiatedDirectly(clazz)
+ || isPinned(clazz.type)
+ || hasAnyInstantiatedLambdas(clazz);
+ }
+
public boolean isPinnedNotProgramOrLibraryOverride(DexReference reference) {
if (isPinned(reference)) {
return true;
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
index 6f2e729..005df04 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -44,11 +44,12 @@
private AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
Class<?> methodToBeKept, Class<?> classToBeKept) throws Exception {
return computeAppViewWithLiveness(
- buildClasses(I.class, J.class, K.class, L.class, A.class).build(),
+ buildClasses(I.class, J.class, K.class, L.class, A.class, Main.class).build(),
factory -> {
List<ProguardConfigurationRule> rules = new ArrayList<>();
rules.addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory));
rules.addAll(buildKeepRuleForClass(classToBeKept, factory));
+ rules.addAll(buildKeepRuleForClassAndMethods(Main.class, factory));
return rules;
});
}
@@ -265,4 +266,11 @@
public interface L extends I, K {}
public static class A implements L {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A();
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InstantiatedLowerBoundTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InstantiatedLowerBoundTest.java
new file mode 100644
index 0000000..6001370
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InstantiatedLowerBoundTest.java
@@ -0,0 +1,165 @@
+// Copyright (c) 2020, 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.resolution.virtualtargets;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InstantiatedLowerBoundTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public InstantiatedLowerBoundTest(TestParameters parameters) {
+ // Empty to satisfy construction of none-runtime.
+ }
+
+ @Test
+ public void testSingleTargetLowerBoundInstantiated() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, B.class, Main.class).build(),
+ factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexType typeA = buildType(A.class, appInfo.dexItemFactory());
+ DexType typeB = buildType(B.class, appInfo.dexItemFactory());
+ DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ ClassTypeLatticeElement latticeB =
+ ClassTypeLatticeElement.create(typeB, Nullability.definitelyNotNull(), appView);
+ DexEncodedMethod singleTarget =
+ appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeB);
+ assertNotNull(singleTarget);
+ DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
+ assertEquals(fooB, singleTarget.method);
+ }
+
+ @Test
+ public void testSingleTargetLowerBoundInMiddleInstantiated() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, B.class, C.class, Main.class).build(),
+ factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexType typeA = buildType(A.class, appInfo.dexItemFactory());
+ DexType typeB = buildType(B.class, appInfo.dexItemFactory());
+ DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ ClassTypeLatticeElement latticeB =
+ ClassTypeLatticeElement.create(typeB, Nullability.definitelyNotNull(), appView);
+ DexEncodedMethod singleTarget =
+ appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeB);
+ assertNotNull(singleTarget);
+ DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
+ assertEquals(fooB, singleTarget.method);
+ }
+
+ @Test
+ public void testSingleTargetLowerAllInstantiated() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, B.class, C.class, MainAllInstantiated.class).build(),
+ factory ->
+ new ArrayList<>(
+ buildKeepRuleForClassAndMethods(MainAllInstantiated.class, factory)));
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexType typeA = buildType(A.class, appInfo.dexItemFactory());
+ DexType typeC = buildType(C.class, appInfo.dexItemFactory());
+ DexType typeMain = buildType(MainAllInstantiated.class, appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooC = buildNullaryVoidMethod(C.class, "foo", appInfo.dexItemFactory());
+ ResolutionResult resolution = appInfo.resolveMethod(typeA, fooA);
+ DexProgramClass context = appView.definitionForProgramType(typeMain);
+ DexProgramClass upperBound = appView.definitionForProgramType(typeA);
+ DexProgramClass lowerBound = appView.definitionForProgramType(typeC);
+ LookupResult lookupResult =
+ resolution.lookupVirtualDispatchTargets(context, appInfo, upperBound, lowerBound);
+ Set<DexMethod> expected = Sets.newIdentityHashSet();
+ expected.add(fooA);
+ expected.add(fooB);
+ expected.add(fooC);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ Set<DexMethod> actual = Sets.newIdentityHashSet();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ clazzAndMethod -> actual.add(clazzAndMethod.getMethod().method),
+ lambdaTarget -> {
+ assert false;
+ });
+ assertEquals(expected, actual);
+ ClassTypeLatticeElement latticeC =
+ ClassTypeLatticeElement.create(typeC, Nullability.definitelyNotNull(), appView);
+ DexEncodedMethod singleTarget =
+ appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeC);
+ assertNull(singleTarget);
+ }
+
+ public static class A {
+
+ public void foo() {
+ System.out.println("A.foo");
+ }
+ }
+
+ public static class B extends A {
+
+ @Override
+ public void foo() {
+ System.out.println("B.foo");
+ }
+ }
+
+ public static class C extends B {
+
+ @Override
+ public void foo() {
+ System.out.println("C.foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new B();
+ }
+ }
+
+ public static class MainAllInstantiated {
+
+ public static void main(String[] args) {
+ new A();
+ new B();
+ new C();
+ }
+ }
+}