Check if methods are overriding if they are seen when marking live

Bug: 182456011
Bug: 182458496
Bug: 182444403
Change-Id: I26a6a30cd7d42f05e9121876417ebf3ba2892ba1
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 331466b..8c70605 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5,11 +5,13 @@
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO;
+import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding;
 import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
 import static com.android.tools.r8.ir.optimize.enums.UnboxedEnumMemberRelocator.ENUM_UNBOXING_UTILITY_CLASS_SUFFIX;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
 import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
+import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
 
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.cf.code.CfInstruction;
@@ -140,7 +142,6 @@
 import java.util.Collections;
 import java.util.Deque;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -2363,11 +2364,13 @@
    */
   private void transitionMethodsForInstantiatedObject(
       InstantiatedObject instantiation, DexClass clazz, List<DexType> interfaces) {
-    Set<Wrapper<DexMethod>> seen = new HashSet<>();
+    Map<Wrapper<DexMethod>, List<DexEncodedMethod>> seen = new HashMap<>();
     WorkList<DexType> worklist = WorkList.newIdentityWorkList();
     worklist.addIfNotSeen(interfaces);
     // First we lookup and mark all targets on the instantiated class for each reachable method in
     // the super chain (inclusive).
+    // TODO(b/182458496): Consider changing this algorithm to a version that collects all reachable
+    //  methods and then does lookup.
     DexClass initialClass = clazz;
     while (clazz != null) {
       if (clazz.isProgramClass()) {
@@ -2405,14 +2408,25 @@
   private void markProgramMethodOverridesAsLive(
       InstantiatedObject instantiation,
       DexClass initialClass,
-      DexProgramClass superClass,
-      Set<Wrapper<DexMethod>> seenMethods) {
-    for (DexMethod method : getReachableVirtualTargets(superClass)) {
-      assert method.holder == superClass.type;
+      DexProgramClass currentClass,
+      Map<Wrapper<DexMethod>, List<DexEncodedMethod>> seenMethods) {
+    for (DexMethod method : getReachableVirtualTargets(currentClass)) {
+      assert method.holder == currentClass.type;
       Wrapper<DexMethod> signature = MethodSignatureEquivalence.get().wrap(method);
-      if (!seenMethods.contains(signature)) {
+      // TODO(b/182456011): Could reachableVirtalTarget be dex encoded methods.
+      DexEncodedMethod currentTarget = definitionFor(method, currentClass);
+      List<DexEncodedMethod> potentialOverrides =
+          seenMethods.computeIfAbsent(signature, ignoreArgument(ArrayList::new));
+      boolean notOverridingCurrentTarget = potentialOverrides.isEmpty();
+      for (DexEncodedMethod potentialOverride : potentialOverrides) {
+        if (!isOverriding(currentTarget, potentialOverride)) {
+          notOverridingCurrentTarget = true;
+          break;
+        }
+      }
+      if (notOverridingCurrentTarget) {
         SingleResolutionResult resolution =
-            appInfo.resolveMethodOn(superClass, method).asSingleResolution();
+            appInfo.resolveMethodOn(currentClass, method).asSingleResolution();
         assert resolution != null;
         assert resolution.getResolvedHolder().isProgramClass();
         if (resolution != null) {
@@ -2420,11 +2434,11 @@
               || resolution
                   .isAccessibleForVirtualDispatchFrom(initialClass.asProgramClass(), appInfo)
                   .isTrue()) {
-            seenMethods.add(signature);
+            potentialOverrides.add(currentTarget);
           }
           if (resolution.getResolvedHolder().isProgramClass()) {
             markLiveOverrides(
-                instantiation, superClass, resolution.getResolutionPair().asProgramMethod());
+                instantiation, currentClass, resolution.getResolutionPair().asProgramMethod());
           }
         }
       }
diff --git a/src/main/java/com/android/tools/r8/utils/FunctionUtils.java b/src/main/java/com/android/tools/r8/utils/FunctionUtils.java
index 3338dde..acd5cb3 100644
--- a/src/main/java/com/android/tools/r8/utils/FunctionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FunctionUtils.java
@@ -7,6 +7,7 @@
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.Supplier;
 
 public class FunctionUtils {
 
@@ -28,4 +29,8 @@
       func.apply(t).accept(argument);
     }
   }
+
+  public static <T, R> Function<T, R> ignoreArgument(Supplier<R> supplier) {
+    return ignore -> supplier.get();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/AbstractSuperClassLiveMethodTest.java b/src/test/java/com/android/tools/r8/shaking/AbstractSuperClassLiveMethodTest.java
index 8cdd48e..f9583f7 100644
--- a/src/test/java/com/android/tools/r8/shaking/AbstractSuperClassLiveMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/AbstractSuperClassLiveMethodTest.java
@@ -69,8 +69,7 @@
         .applyIf(
             parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
             r -> r.assertSuccessWithOutputLines(EXPECTED_DALVIK),
-            // TODO(b/182444403): Should succeed with EXPECTED.
-            r -> r.assertFailureWithErrorThatThrows(AbstractMethodError.class));
+            r -> r.assertSuccessWithOutputLines(EXPECTED));
   }
 
   @NoVerticalClassMerging