Introduce MultipleResolutionResult as a method resolution result

Bug: b/226170842
Bug: b/214382176
Change-Id: I504c916f1b158bb397e95e17e2e87185a1c155bf
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 4c28ff0..81979c8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -532,17 +532,17 @@
    * may be abstract.
    */
   public DexClassAndMethod lookupMaximallySpecificMethod(DexClass clazz, DexMethod method) {
-    return new MethodResolution(this::definitionFor, dexItemFactory())
+    return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
         .lookupMaximallySpecificTarget(clazz, method);
   }
 
   MethodResolutionResult resolveMaximallySpecificTarget(DexClass clazz, DexMethod method) {
-    return new MethodResolution(this::definitionFor, dexItemFactory())
+    return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
         .resolveMaximallySpecificTarget(clazz, method);
   }
 
   MethodResolutionResult resolveMaximallySpecificTarget(LambdaDescriptor lambda, DexMethod method) {
-    return new MethodResolution(this::definitionFor, dexItemFactory())
+    return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
         .resolveMaximallySpecificTarget(lambda, method);
   }
 
@@ -648,7 +648,7 @@
    */
   public MethodResolutionResult unsafeResolveMethodDueToDexFormat(DexMethod method) {
     assert checkIfObsolete();
-    return new MethodResolution(this::definitionFor, dexItemFactory())
+    return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
         .unsafeResolveMethodDueToDexFormat(method);
   }
 
@@ -698,7 +698,7 @@
   public MethodResolutionResult resolveMethodOnClass(
       DexType holder, DexProto proto, DexString name) {
     assert checkIfObsolete();
-    return new MethodResolution(this::definitionFor, dexItemFactory())
+    return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
         .resolveMethodOnClass(holder, proto, name);
   }
 
@@ -715,7 +715,7 @@
   public MethodResolutionResult resolveMethodOnClass(
       DexClass clazz, DexProto proto, DexString name) {
     assert checkIfObsolete();
-    return new MethodResolution(this::definitionFor, dexItemFactory())
+    return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
         .resolveMethodOnClass(clazz, proto, name);
   }
 
@@ -726,7 +726,7 @@
 
   public MethodResolutionResult resolveMethodOnInterface(DexType holder, DexMethod method) {
     assert checkIfObsolete();
-    return new MethodResolution(this::definitionFor, dexItemFactory())
+    return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
         .resolveMethodOnInterface(holder, method.getProto(), method.getName());
   }
 
@@ -744,7 +744,7 @@
   public MethodResolutionResult resolveMethodOnInterface(
       DexClass clazz, DexProto proto, DexString name) {
     assert checkIfObsolete();
-    return new MethodResolution(this::definitionFor, dexItemFactory())
+    return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
         .resolveMethodOnInterface(clazz, proto, name);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
index f11571a..12baacf 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
@@ -21,6 +21,10 @@
 
   void forEachClassResolutionResult(Consumer<DexClass> consumer);
 
+  default boolean isMultipleClassResolutionResult() {
+    return false;
+  }
+
   static Builder builder() {
     return new Builder();
   }
@@ -122,6 +126,11 @@
       consumer.accept(programOrClasspathClass);
       consumer.accept(libraryClass);
     }
+
+    @Override
+    public boolean isMultipleClassResolutionResult() {
+      return true;
+    }
   }
 
   class ProgramAndLibraryClassResolutionResult
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolution.java b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
index 2f6939e..0927de2 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolution.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.MethodResolutionResult.ArrayCloneMethodResult;
 import com.android.tools.r8.graph.MethodResolutionResult.ClassNotFoundResult;
 import com.android.tools.r8.graph.MethodResolutionResult.IllegalAccessOrNoSuchMethodResult;
@@ -27,15 +28,31 @@
  */
 public class MethodResolution {
 
-  private final Function<DexType, DexClass> definitionFor;
+  private final Function<DexType, ClassResolutionResult> definitionFor;
   private final DexItemFactory factory;
+  private final boolean escapeIfLibraryHasProgramSuperType;
 
-  public MethodResolution(Function<DexType, DexClass> definitionFor, DexItemFactory factory) {
+  private MethodResolution(
+      Function<DexType, ClassResolutionResult> definitionFor,
+      DexItemFactory factory,
+      boolean escapeIfLibraryHasProgramSuperType) {
     this.definitionFor = definitionFor;
     this.factory = factory;
+    this.escapeIfLibraryHasProgramSuperType = escapeIfLibraryHasProgramSuperType;
   }
 
-  private DexClass definitionFor(DexType type) {
+  public static MethodResolution createLegacy(
+      Function<DexType, DexClass> definitionFor, DexItemFactory factory) {
+    return new MethodResolution(
+        type -> {
+          DexClass clazz = definitionFor.apply(type);
+          return clazz == null ? ClassResolutionResult.NoResolutionResult.noResult() : clazz;
+        },
+        factory,
+        false);
+  }
+
+  private ClassResolutionResult definitionFor(DexType type) {
     return definitionFor.apply(type);
   }
 
@@ -53,14 +70,19 @@
     if (holder.isArrayType()) {
       return resolveMethodOnArray(holder, method.getProto(), method.getName());
     }
-    DexClass definition = definitionFor(holder);
-    if (definition == null) {
-      return ClassNotFoundResult.INSTANCE;
-    } else if (definition.isInterface()) {
-      return resolveMethodOnInterface(definition, method.getProto(), method.getName());
-    } else {
-      return resolveMethodOnClass(definition, method.getProto(), method.getName());
-    }
+    MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
+    definitionFor(holder)
+        .forEachClassResolutionResult(
+            clazz -> {
+              if (clazz.isInterface()) {
+                builder.addResolutionResult(
+                    resolveMethodOnInterface(clazz, method.getProto(), method.getName()));
+              } else {
+                builder.addResolutionResult(
+                    resolveMethodOnClass(clazz, method.getProto(), method.getName()));
+              }
+            });
+    return builder.buildOrIfEmpty(ClassNotFoundResult.INSTANCE);
   }
 
   /**
@@ -96,15 +118,18 @@
     if (holder.isArrayType()) {
       return resolveMethodOnArray(holder, methodProto, methodName);
     }
-    DexClass clazz = definitionFor(holder);
-    if (clazz == null) {
-      return ClassNotFoundResult.INSTANCE;
-    }
-    // Step 1: If holder is an interface, resolution fails with an ICCE. We return null.
-    if (clazz.isInterface()) {
-      return IncompatibleClassResult.INSTANCE;
-    }
-    return resolveMethodOnClass(clazz, methodProto, methodName);
+    MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
+    definitionFor(holder)
+        .forEachClassResolutionResult(
+            clazz -> {
+              // Step 1: If holder is an interface, resolution fails with an ICCE.
+              if (clazz.isInterface()) {
+                builder.addResolutionResult(IncompatibleClassResult.INSTANCE);
+              } else {
+                builder.addResolutionResult(resolveMethodOnClass(clazz, methodProto, methodName));
+              }
+            });
+    return builder.buildOrIfEmpty(ClassNotFoundResult.INSTANCE);
   }
 
   public MethodResolutionResult resolveMethodOnClass(
@@ -135,7 +160,8 @@
     // Section 2.9 of the JVM Spec</a>.
     DexEncodedMethod result = clazz.lookupSignaturePolymorphicMethod(methodName, factory);
     if (result != null) {
-      return new SingleResolutionResult(initialResolutionHolder, clazz, result);
+      return MethodResolutionResult.createSingleResolutionResult(
+          initialResolutionHolder, clazz, result);
     }
     // Pt 2: Find a method that matches the descriptor.
     result = clazz.lookupMethod(methodProto, methodName);
@@ -148,17 +174,27 @@
       if (result.isPrivateMethod() && clazz != initialResolutionHolder) {
         return new IllegalAccessOrNoSuchMethodResult(initialResolutionHolder, result);
       }
-      return new SingleResolutionResult(initialResolutionHolder, clazz, result);
+      return MethodResolutionResult.createSingleResolutionResult(
+          initialResolutionHolder, clazz, result);
     }
     // Pt 3: Apply step two to direct superclass of holder.
+    MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
     if (clazz.superType != null) {
-      DexClass superClass = definitionFor(clazz.superType);
-      if (superClass != null) {
-        return resolveMethodOnClassStep2(
-            superClass, methodProto, methodName, initialResolutionHolder);
-      }
+      definitionFor(clazz.superType)
+          .forEachClassResolutionResult(
+              superClass -> {
+                // Guard against going back into the program for resolution.
+                if (escapeIfLibraryHasProgramSuperType
+                    && clazz.isLibraryClass()
+                    && !superClass.isLibraryClass()) {
+                  return;
+                }
+                builder.addResolutionResult(
+                    resolveMethodOnClassStep2(
+                        superClass, methodProto, methodName, initialResolutionHolder));
+              });
     }
-    return null;
+    return builder.buildOrIfEmpty(null);
   }
 
   /**
@@ -201,7 +237,7 @@
     MaximallySpecificMethodsBuilder builder =
         new MaximallySpecificMethodsBuilder(definitionFor, factory);
     resolveMethodStep3Helper(
-        method.getProto(), method.getName(), factory.objectType, lambda.interfaces, builder);
+        method.getProto(), method.getName(), null, builder, factory.objectType, lambda.interfaces);
     return builder;
   }
 
@@ -212,37 +248,57 @@
       DexClass clazz,
       MaximallySpecificMethodsBuilder builder) {
     resolveMethodStep3Helper(
-        methodProto, methodName, clazz.superType, Arrays.asList(clazz.interfaces.values), builder);
+        methodProto,
+        methodName,
+        clazz,
+        builder,
+        clazz.superType,
+        Arrays.asList(clazz.interfaces.values));
   }
 
   private void resolveMethodStep3Helper(
       DexProto methodProto,
       DexString methodName,
+      DexClass clazz,
+      MaximallySpecificMethodsBuilder builder,
       DexType superType,
-      List<DexType> interfaces,
-      MaximallySpecificMethodsBuilder builder) {
+      List<DexType> interfaces) {
     for (DexType iface : interfaces) {
-      DexClass definition = definitionFor(iface);
-      if (definition == null) {
-        // Ignore missing interface definitions.
-        continue;
+      ClassResolutionResult classResolutionResult = definitionFor(iface);
+      if (classResolutionResult.isMultipleClassResolutionResult()) {
+        // TODO(b/214382176, b/226170842): Compute maximal specific set in precense of
+        //   multiple results.
+        throw new Unreachable(
+            "MethodResolution should not be passed definition with multiple results");
       }
-      assert definition.isInterface();
-      DexEncodedMethod result = definition.lookupMethod(methodProto, methodName);
-      if (isMaximallySpecificCandidate(result)) {
-        // The candidate is added and doing so will prohibit shadowed methods from being in the set.
-        builder.addCandidate(definition, result);
-      } else {
-        // Look at the super-interfaces of this class and keep searching.
-        resolveMethodStep3Helper(methodProto, methodName, definition, builder);
-      }
+      classResolutionResult.forEachClassResolutionResult(
+          definition -> {
+            assert definition.isInterface();
+            DexEncodedMethod result = definition.lookupMethod(methodProto, methodName);
+            if (isMaximallySpecificCandidate(result)) {
+              // The candidate is added and doing so will prohibit shadowed methods from being
+              // in the set.
+              builder.addCandidate(definition, result);
+            } else {
+              // Look at the super-interfaces of this class and keep searching.
+              resolveMethodStep3Helper(methodProto, methodName, definition, builder);
+            }
+          });
     }
     // Now look at indirect super interfaces.
     if (superType != null) {
-      DexClass superClass = definitionFor(superType);
-      if (superClass != null) {
-        resolveMethodStep3Helper(methodProto, methodName, superClass, builder);
-      }
+      definitionFor(superType)
+          .forEachClassResolutionResult(
+              superClass -> {
+                // Guard against going back into the program for resolution.
+                if (escapeIfLibraryHasProgramSuperType
+                    && clazz != null
+                    && clazz.isLibraryClass()
+                    && !superClass.isLibraryClass()) {
+                  return;
+                }
+                resolveMethodStep3Helper(methodProto, methodName, superClass, builder);
+              });
     }
   }
 
@@ -271,17 +327,20 @@
     if (holder.isArrayType()) {
       return IncompatibleClassResult.INSTANCE;
     }
+    MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
     // Step 1: Lookup interface.
-    DexClass definition = definitionFor(holder);
-    // If the definition is not an interface, resolution fails with an ICCE. We just return the
-    // empty result here.
-    if (definition == null) {
-      return ClassNotFoundResult.INSTANCE;
-    }
-    if (!definition.isInterface()) {
-      return IncompatibleClassResult.INSTANCE;
-    }
-    return resolveMethodOnInterface(definition, proto, methodName);
+    definitionFor(holder)
+        .forEachClassResolutionResult(
+            definition -> {
+              // If the definition is not an interface, resolution fails with an ICCE.
+              if (!definition.isInterface()) {
+                builder.addResolutionResult(IncompatibleClassResult.INSTANCE);
+              } else {
+                builder.addResolutionResult(
+                    resolveMethodOnInterface(definition, proto, methodName));
+              }
+            });
+    return builder.buildOrIfEmpty(ClassNotFoundResult.INSTANCE);
   }
 
   public MethodResolutionResult resolveMethodOnInterface(
@@ -290,20 +349,28 @@
     // Step 2: Look for exact method on interface.
     DexEncodedMethod result = definition.lookupMethod(methodProto, methodName);
     if (result != null) {
-      return new SingleResolutionResult(definition, definition, result);
+      return MethodResolutionResult.createSingleResolutionResult(definition, definition, result);
     }
     // Step 3: Look for matching method on object class.
-    DexClass objectClass = definitionFor(factory.objectType);
-    if (objectClass == null) {
-      return ClassNotFoundResult.INSTANCE;
-    }
-    result = objectClass.lookupMethod(methodProto, methodName);
-    if (result != null && result.accessFlags.isPublic() && !result.accessFlags.isAbstract()) {
-      return new SingleResolutionResult(definition, objectClass, result);
-    }
-    // Step 3: Look for maximally-specific superinterface methods or any interface definition.
-    //         This is the same for classes and interfaces.
-    return resolveMethodStep3(definition, methodProto, methodName);
+    MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
+    definitionFor(factory.objectType)
+        .forEachClassResolutionResult(
+            objectClass -> {
+              DexEncodedMethod objectResult = objectClass.lookupMethod(methodProto, methodName);
+              if (objectResult != null
+                  && objectResult.accessFlags.isPublic()
+                  && !objectResult.accessFlags.isAbstract()) {
+                builder.addResolutionResult(
+                    MethodResolutionResult.createSingleResolutionResult(
+                        definition, objectClass, objectResult));
+              } else {
+                // Step 3: Look for maximally-specific superinterface methods or any interface
+                // definition. This is the same for classes and interfaces.
+                builder.addResolutionResult(
+                    resolveMethodStep3(definition, methodProto, methodName));
+              }
+            });
+    return builder.buildOrIfEmpty(ClassNotFoundResult.INSTANCE);
   }
 
   static class MaximallySpecificMethodsBuilder {
@@ -316,11 +383,11 @@
     // prior to writing.
     private final LinkedHashMap<DexClass, DexEncodedMethod> maximallySpecificMethods =
         new LinkedHashMap<>();
-    private final Function<DexType, DexClass> definitionFor;
+    private final Function<DexType, ClassResolutionResult> definitionFor;
     private final DexItemFactory factory;
 
     private MaximallySpecificMethodsBuilder(
-        Function<DexType, DexClass> definitionFor, DexItemFactory factory) {
+        Function<DexType, ClassResolutionResult> definitionFor, DexItemFactory factory) {
       this.definitionFor = definitionFor;
       this.factory = factory;
     }
@@ -343,22 +410,29 @@
       if (type == null) {
         return;
       }
-      DexClass clazz = definitionFor.apply(type);
-      if (clazz == null) {
-        return;
+      ClassResolutionResult classResolutionResult = definitionFor.apply(type);
+      if (classResolutionResult.isMultipleClassResolutionResult()) {
+        // TODO(b/214382176, b/226170842): Compute maximal specific set in precense of
+        //   multiple results.
+        throw new Unreachable(
+            "MethodResolution should not be passed definition with multiple results");
       }
-      assert clazz.isInterface();
-      assert clazz.superType == factory.objectType;
-      // A null entry signifies that the candidate is shadowed blocking future candidates.
-      // If the candidate is already shadowed at this type there is no need to shadow further up.
-      if (maximallySpecificMethods.containsKey(clazz)
-          && maximallySpecificMethods.get(clazz) == null) {
-        return;
-      }
-      maximallySpecificMethods.put(clazz, null);
-      for (DexType iface : clazz.interfaces.values) {
-        markShadowed(iface);
-      }
+      classResolutionResult.forEachClassResolutionResult(
+          clazz -> {
+            assert clazz.isInterface();
+            assert clazz.superType == factory.objectType;
+            // A null entry signifies that the candidate is shadowed blocking future candidates.
+            // If the candidate is already shadowed at this type there is no need to shadow
+            // further up.
+            if (maximallySpecificMethods.containsKey(clazz)
+                && maximallySpecificMethods.get(clazz) == null) {
+              return;
+            }
+            maximallySpecificMethods.put(clazz, null);
+            for (DexType iface : clazz.interfaces.values) {
+              markShadowed(iface);
+            }
+          });
     }
 
     DexClassAndMethod lookup() {
@@ -407,9 +481,9 @@
       return IncompatibleClassResult.create(ListUtils.map(nonAbstractMethods, Entry::getValue));
     }
 
-    private static SingleResolutionResult singleResultHelper(
+    private static SingleResolutionResult<?> singleResultHelper(
         DexClass initialResolutionResult, Entry<DexClass, DexEncodedMethod> entry) {
-      return new SingleResolutionResult(
+      return MethodResolutionResult.createSingleResolutionResult(
           initialResolutionResult != null ? initialResolutionResult : entry.getKey(),
           entry.getKey(),
           entry.getValue());
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index 49860a7..65ff279 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
 import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
@@ -12,9 +13,13 @@
 import com.android.tools.r8.shaking.InstantiatedObject;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ConsumerUtils;
 import com.android.tools.r8.utils.OptionalBool;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
@@ -43,7 +48,7 @@
   }
 
   /** Returns non-null if isSingleResolution() is true, otherwise null. */
-  public SingleResolutionResult asSingleResolution() {
+  public SingleResolutionResult<?> asSingleResolution() {
     return null;
   }
 
@@ -159,17 +164,49 @@
       AppInfoWithClassHierarchy appInfo,
       Consumer<? super DexEncodedMethod> methodCausingFailureConsumer);
 
+  public abstract void visitMethodResolutionResults(
+      Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+      Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+      Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+      Consumer<? super FailedResolutionResult> failedResolutionConsumer);
+
+  public boolean hasProgramResult() {
+    return false;
+  }
+
+  public SingleClasspathResolutionResult asSingleClasspathResolutionResult() {
+    return null;
+  }
+
+  protected SingleProgramResolutionResult asSingleProgramResolutionResult() {
+    return null;
+  }
+
+  public static SingleResolutionResult<?> createSingleResolutionResult(
+      DexClass initialResolutionHolder, DexClass holder, DexEncodedMethod definition) {
+    if (holder.isLibraryClass()) {
+      return new SingleLibraryResolutionResult(
+          initialResolutionHolder, holder.asLibraryClass(), definition);
+    } else if (holder.isClasspathClass()) {
+      return new SingleClasspathResolutionResult(
+          initialResolutionHolder, holder.asClasspathClass(), definition);
+    } else {
+      assert holder.isProgramClass();
+      return new SingleProgramResolutionResult(
+          initialResolutionHolder, holder.asProgramClass(), definition);
+    }
+  }
+
   /** Result for a resolution that succeeds with a known declaration/definition. */
-  public static class SingleResolutionResult extends MethodResolutionResult
+  public abstract static class SingleResolutionResult<T extends DexClass>
+      extends MethodResolutionResult
       implements SuccessfulMemberResolutionResult<DexEncodedMethod, DexMethod> {
     private final DexClass initialResolutionHolder;
-    private final DexClass resolvedHolder;
+    private final T resolvedHolder;
     private final DexEncodedMethod resolvedMethod;
 
     public SingleResolutionResult(
-        DexClass initialResolutionHolder,
-        DexClass resolvedHolder,
-        DexEncodedMethod resolvedMethod) {
+        DexClass initialResolutionHolder, T resolvedHolder, DexEncodedMethod resolvedMethod) {
       assert initialResolutionHolder != null;
       assert resolvedHolder != null;
       assert resolvedMethod != null;
@@ -181,11 +218,8 @@
           || initialResolutionHolder.type == resolvedMethod.getHolderType();
     }
 
-    public SingleResolutionResult withInitialResolutionHolder(DexClass newInitialResolutionHolder) {
-      return newInitialResolutionHolder != initialResolutionHolder
-          ? new SingleResolutionResult(newInitialResolutionHolder, resolvedHolder, resolvedMethod)
-          : this;
-    }
+    public abstract SingleResolutionResult<T> withInitialResolutionHolder(
+        DexClass newInitialResolutionHolder);
 
     @Override
     public DexClass getInitialResolutionHolder() {
@@ -193,7 +227,7 @@
     }
 
     @Override
-    public DexClass getResolvedHolder() {
+    public T getResolvedHolder() {
       return resolvedHolder;
     }
 
@@ -225,7 +259,7 @@
     }
 
     @Override
-    public SingleResolutionResult asSingleResolution() {
+    public SingleResolutionResult<?> asSingleResolution() {
       return this;
     }
 
@@ -778,6 +812,108 @@
     }
   }
 
+  public static class SingleProgramResolutionResult
+      extends SingleResolutionResult<DexProgramClass> {
+
+    public SingleProgramResolutionResult(
+        DexClass initialResolutionHolder,
+        DexProgramClass resolvedHolder,
+        DexEncodedMethod resolvedMethod) {
+      super(initialResolutionHolder, resolvedHolder, resolvedMethod);
+    }
+
+    @Override
+    public SingleResolutionResult<DexProgramClass> withInitialResolutionHolder(
+        DexClass newInitialResolutionHolder) {
+      return newInitialResolutionHolder != getInitialResolutionHolder()
+          ? new SingleProgramResolutionResult(
+              newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod())
+          : this;
+    }
+
+    @Override
+    public void visitMethodResolutionResults(
+        Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+        Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+        Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+      programOrClasspathConsumer.accept(this);
+    }
+
+    @Override
+    public boolean hasProgramResult() {
+      return true;
+    }
+
+    @Override
+    protected SingleProgramResolutionResult asSingleProgramResolutionResult() {
+      return this;
+    }
+  }
+
+  public static class SingleClasspathResolutionResult
+      extends SingleResolutionResult<DexClasspathClass> {
+
+    public SingleClasspathResolutionResult(
+        DexClass initialResolutionHolder,
+        DexClasspathClass resolvedHolder,
+        DexEncodedMethod resolvedMethod) {
+      super(initialResolutionHolder, resolvedHolder, resolvedMethod);
+    }
+
+    @Override
+    public SingleClasspathResolutionResult withInitialResolutionHolder(
+        DexClass newInitialResolutionHolder) {
+      return newInitialResolutionHolder != getInitialResolutionHolder()
+          ? new SingleClasspathResolutionResult(
+              newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod())
+          : this;
+    }
+
+    @Override
+    public void visitMethodResolutionResults(
+        Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+        Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+        Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+      programOrClasspathConsumer.accept(this);
+    }
+
+    @Override
+    public SingleClasspathResolutionResult asSingleClasspathResolutionResult() {
+      return this;
+    }
+  }
+
+  public static class SingleLibraryResolutionResult
+      extends SingleResolutionResult<DexLibraryClass> {
+
+    public SingleLibraryResolutionResult(
+        DexClass initialResolutionHolder,
+        DexLibraryClass resolvedHolder,
+        DexEncodedMethod resolvedMethod) {
+      super(initialResolutionHolder, resolvedHolder, resolvedMethod);
+    }
+
+    @Override
+    public SingleLibraryResolutionResult withInitialResolutionHolder(
+        DexClass newInitialResolutionHolder) {
+      return newInitialResolutionHolder != getInitialResolutionHolder()
+          ? new SingleLibraryResolutionResult(
+              newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod())
+          : this;
+    }
+
+    @Override
+    public void visitMethodResolutionResults(
+        Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+        Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+        Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+      libraryResultConsumer.accept(this);
+    }
+  }
+
   abstract static class EmptyResult extends MethodResolutionResult {
 
     @Override
@@ -870,6 +1006,15 @@
     }
 
     @Override
+    public void visitMethodResolutionResults(
+        Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+        Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+        Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+      cloneResultConsumer.accept(this);
+    }
+
+    @Override
     public boolean isArrayCloneMethodResult() {
       return true;
     }
@@ -913,6 +1058,15 @@
     public boolean hasMethodsCausingError() {
       return false;
     }
+
+    @Override
+    public void visitMethodResolutionResults(
+        Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+        Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+        Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+      failedResolutionConsumer.accept(this);
+    }
   }
 
   public static class ClassNotFoundResult extends FailedResolutionResult {
@@ -1038,4 +1192,230 @@
     }
   }
 
+  public abstract static class MultipleMethodResolutionResult<
+          C extends DexClass & ProgramOrClasspathClass, T extends SingleResolutionResult<C>>
+      extends MethodResolutionResult {
+
+    protected final T programOrClasspathResult;
+    protected final List<SingleLibraryResolutionResult> libraryResolutionResults;
+    protected final List<FailedResolutionResult> failedResolutionResults;
+
+    public MultipleMethodResolutionResult(
+        T programOrClasspathResult,
+        List<SingleLibraryResolutionResult> libraryResolutionResults,
+        List<FailedResolutionResult> failedResolutionResults) {
+      this.programOrClasspathResult = programOrClasspathResult;
+      this.libraryResolutionResults = libraryResolutionResults;
+      this.failedResolutionResults = failedResolutionResults;
+    }
+
+    @Override
+    public OptionalBool isAccessibleFrom(
+        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public OptionalBool isAccessibleForVirtualDispatchFrom(
+        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public boolean isVirtualTarget() {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public DexClassAndMethod lookupInvokeSpecialTarget(
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public DexClassAndMethod lookupInvokeSuperTarget(
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public DexEncodedMethod lookupInvokeDirectTarget(
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public DexEncodedMethod lookupInvokeStaticTarget(
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public LookupResult lookupVirtualDispatchTargets(
+        DexProgramClass context,
+        AppInfoWithClassHierarchy appInfo,
+        InstantiatedSubTypeInfo instantiatedInfo,
+        PinnedPredicate pinnedPredicate) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public LookupResult lookupVirtualDispatchTargets(
+        DexProgramClass context,
+        AppInfoWithLiveness appInfo,
+        DexProgramClass refinedReceiverUpperBound,
+        DexProgramClass refinedReceiverLowerBound) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public LookupTarget lookupVirtualDispatchTarget(
+        InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public DexClassAndMethod lookupVirtualDispatchTarget(
+        DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public LookupTarget lookupVirtualDispatchTarget(
+        LambdaDescriptor lambdaInstance,
+        AppInfoWithClassHierarchy appInfo,
+        Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
+      throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public void visitMethodResolutionResults(
+        Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+        Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+        Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+      if (programOrClasspathResult != null) {
+        programOrClasspathConsumer.accept(programOrClasspathResult);
+      }
+      libraryResolutionResults.forEach(libraryResultConsumer);
+      failedResolutionResults.forEach(failedResolutionConsumer);
+    }
+  }
+
+  public static class MultipleProgramWithLibraryResolutionResult
+      extends MultipleMethodResolutionResult<DexProgramClass, SingleProgramResolutionResult> {
+
+    public MultipleProgramWithLibraryResolutionResult(
+        SingleProgramResolutionResult programOrClasspathResult,
+        List<SingleLibraryResolutionResult> libraryResolutionResults,
+        List<FailedResolutionResult> failedOrUnknownResolutionResults) {
+      super(programOrClasspathResult, libraryResolutionResults, failedOrUnknownResolutionResults);
+    }
+  }
+
+  public static class MultipleClasspathWithLibraryResolutionResult
+      extends MultipleMethodResolutionResult<DexClasspathClass, SingleClasspathResolutionResult> {
+
+    public MultipleClasspathWithLibraryResolutionResult(
+        SingleClasspathResolutionResult programOrClasspathResult,
+        List<SingleLibraryResolutionResult> libraryResolutionResults,
+        List<FailedResolutionResult> failedOrUnknownResolutionResults) {
+      super(programOrClasspathResult, libraryResolutionResults, failedOrUnknownResolutionResults);
+    }
+  }
+
+  public static class MultipleLibraryMethodResolutionResult
+      extends MultipleMethodResolutionResult<DexProgramClass, SingleProgramResolutionResult> {
+
+    public MultipleLibraryMethodResolutionResult(
+        List<SingleLibraryResolutionResult> libraryResolutionResults,
+        List<FailedResolutionResult> failedOrUnknownResolutionResults) {
+      super(null, libraryResolutionResults, failedOrUnknownResolutionResults);
+    }
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private MethodResolutionResult possiblySingleResult = null;
+    private List<MethodResolutionResult> allResults = null;
+
+    private Builder() {}
+
+    public void addResolutionResult(MethodResolutionResult result) {
+      if (possiblySingleResult == null) {
+        possiblySingleResult = result;
+        return;
+      }
+      if (allResults == null) {
+        allResults = new ArrayList<>();
+        allResults.add(possiblySingleResult);
+      }
+      allResults.add(result);
+    }
+
+    public MethodResolutionResult buildOrIfEmpty(MethodResolutionResult emptyResult) {
+      if (possiblySingleResult == null) {
+        return emptyResult;
+      } else if (allResults == null) {
+        return possiblySingleResult;
+      }
+      Box<SingleResolutionResult<?>> singleResult = new Box<>();
+      List<SingleLibraryResolutionResult> libraryResults = new ArrayList<>();
+      List<FailedResolutionResult> failedResults = new ArrayList<>();
+      allResults.forEach(
+          otherResult -> {
+            otherResult.visitMethodResolutionResults(
+                otherProgramOrClasspathResult -> {
+                  if (singleResult.isSet()) {
+                    assert false : "Unexpected multiple results between program and classpath";
+                    if (singleResult.get().hasProgramResult()) {
+                      return;
+                    }
+                  }
+                  singleResult.set(otherProgramOrClasspathResult);
+                },
+                newLibraryResult -> {
+                  if (!Iterables.any(
+                      libraryResults,
+                      existing ->
+                          existing.getResolvedHolder() == newLibraryResult.getResolvedHolder())) {
+                    libraryResults.add(newLibraryResult);
+                  }
+                },
+                ConsumerUtils.emptyConsumer(),
+                newFailedResult -> {
+                  if (!Iterables.any(
+                      failedResults,
+                      existing ->
+                          existing.isFailedResolution() == newFailedResult.isFailedResolution())) {
+                    failedResults.add(newFailedResult);
+                  }
+                });
+          });
+      if (!singleResult.isSet()) {
+        if (libraryResults.size() == 1 && failedResults.isEmpty()) {
+          return libraryResults.get(0);
+        } else if (libraryResults.isEmpty() && failedResults.size() == 1) {
+          return failedResults.get(0);
+        } else {
+          return new MultipleLibraryMethodResolutionResult(libraryResults, failedResults);
+        }
+      } else if (libraryResults.isEmpty() && failedResults.isEmpty()) {
+        return singleResult.get();
+      } else if (singleResult.get().hasProgramResult()) {
+        return new MultipleProgramWithLibraryResolutionResult(
+            singleResult.get().asSingleProgramResolutionResult(), libraryResults, failedResults);
+      } else {
+        SingleClasspathResolutionResult classpathResult =
+            singleResult.get().asSingleClasspathResolutionResult();
+        assert classpathResult != null;
+        return new MultipleClasspathWithLibraryResolutionResult(
+            classpathResult, libraryResults, failedResults);
+      }
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 1103c8f..6c8703b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -57,7 +57,7 @@
     private DexMethod superMethod(
         AppView<? extends AppInfoWithClassHierarchy> appView, MergeGroup group) {
       DexMethod template = methods.iterator().next().getReference();
-      SingleResolutionResult resolutionResult =
+      SingleResolutionResult<?> resolutionResult =
           appView
               .appInfo()
               .resolveMethodOnClass(group.getSuperType(), template)
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
index a914cf9..fecb888 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
@@ -105,7 +105,7 @@
   }
 
   private boolean hasNonAbstractDefinitionInSuperClass(DexType superType, ProgramMethod method) {
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView
             .appInfo()
             .resolveMethodOnClass(superType, method.getReference())
@@ -118,7 +118,7 @@
     return Iterables.any(
         interfaceTypes,
         interfaceType -> {
-          SingleResolutionResult resolutionResult =
+          SingleResolutionResult<?> resolutionResult =
               appView
                   .appInfo()
                   .resolveMethodOnInterface(interfaceType, method.getReference())
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
index 54bcf57..ca52f7c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
@@ -170,7 +170,7 @@
         MethodCategory category = MethodCategory.CLASS_HIERARCHY_SAFE;
         if (clazzReserved.contains(signature)) {
           DexMethod template = signature.withHolder(clazz, appView.dexItemFactory());
-          SingleResolutionResult result =
+          SingleResolutionResult<?> result =
               appView.appInfo().resolveMethodOnClass(clazz, template).asSingleResolution();
           if (result == null || result.getResolvedHolder().isInterface()) {
             category = MethodCategory.KEEP_ABSENT;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index cdf992b..c13800c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -197,7 +197,7 @@
     AppView<? extends AppInfoWithClassHierarchy> appViewWithClassHierarchy =
         appView.withClassHierarchy();
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appViewWithClassHierarchy
             .appInfo()
             .resolveMethod(getInvokedMethod(), getInterfaceBit())
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 7aa20ad..f39baa4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -196,7 +196,7 @@
     AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
     AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appViewWithLiveness
             .appInfo()
             .resolveMethod(getInvokedMethod(), isInterface)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index d01f7fc..012c31c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -381,7 +381,7 @@
             appView.dexItemFactory().icceType,
             descriptor.implHandle.isInterface);
       }
-      SingleResolutionResult result = resolution.asSingleResolution();
+      SingleResolutionResult<?> result = resolution.asSingleResolution();
       assert result.getResolvedMethod().isStatic();
       assert result.getResolvedHolder().isProgramClass();
       return new StaticLambdaImplTarget(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index 48dca2d..8935159 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -123,7 +123,7 @@
             .resolveMethod(bootstrapMethodReference, bootstrapMethodHandle.isInterface);
     if (resolution.isSingleResolution()
         && resolution.asSingleResolution().getResolvedMethod().isStatic()) {
-      SingleResolutionResult result = resolution.asSingleResolution();
+      SingleResolutionResult<?> result = resolution.asSingleResolution();
       if (bootstrapMethodHandle.isInterface
           && appView.options().isInterfaceMethodDesugaringEnabled()) {
         bootstrapMethodReference =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 778ed23..7b3bb07 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -405,7 +405,7 @@
           .build();
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView
             .appInfoForDesugaring()
             .resolveMethodOnInterface(holder, invoke.getMethod())
@@ -435,7 +435,7 @@
   private DesugarDescription computeInvokeVirtualDispatch(
       DexClass holder, CfInvoke invoke, ProgramMethod context) {
     AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring();
-    SingleResolutionResult resolution =
+    SingleResolutionResult<?> resolution =
         appInfoForDesugaring
             .resolveMethod(invoke.getMethod(), invoke.isInterface())
             .asSingleResolution();
@@ -497,7 +497,7 @@
       return computeInvokeAsThrowRewrite(invoke, null, context);
     }
 
-    SingleResolutionResult singleResolution = resolution.asSingleResolution();
+    SingleResolutionResult<?> singleResolution = resolution.asSingleResolution();
     if (singleResolution == null) {
       return DesugarDescription.nothing();
     }
@@ -573,7 +573,7 @@
   }
 
   private DesugarDescription computeInvokeAsThrowRewrite(
-      CfInvoke invoke, SingleResolutionResult resolution, ProgramMethod context) {
+      CfInvoke invoke, SingleResolutionResult<?> resolution, ProgramMethod context) {
     assert !isAlreadyDesugared(invoke, context);
     return AlwaysThrowingInstructionDesugaring.computeInvokeAsThrowRewrite(
         appView, invoke, resolution);
@@ -637,7 +637,7 @@
           .build();
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView.appInfoForDesugaring().resolveMethodOn(clazz, invokedMethod).asSingleResolution();
     if (clazz.isInterface() && shouldRewriteToInvokeToThrow(resolutionResult, false)) {
       return computeInvokeAsThrowRewrite(invoke, resolutionResult, context);
@@ -760,7 +760,7 @@
   }
 
   private boolean shouldRewriteToInvokeToThrow(
-      SingleResolutionResult resolutionResult, boolean isInvokeStatic) {
+      SingleResolutionResult<?> resolutionResult, boolean isInvokeStatic) {
     return resolutionResult == null
         || resolutionResult.getResolvedMethod().isStatic() != isInvokeStatic;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index 97c8d47..1c49fc9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -227,7 +227,7 @@
 
   private boolean computeAssumedValuesFromSingleTarget(
       IRCode code, InvokeMethod invoke, AssumedValues.Builder assumedValuesBuilder) {
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView
             .appInfo()
             .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 9490667..3788e98 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -124,7 +124,7 @@
   @Override
   public boolean passesInliningConstraints(
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethod singleTarget,
       Reason reason,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
@@ -261,7 +261,7 @@
   public InlineResult computeInlining(
       IRCode code,
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethod singleTarget,
       ProgramMethod context,
       ClassInitializationAnalysis classInitializationAnalysis,
@@ -336,7 +336,7 @@
 
   private boolean neverInline(
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethod singleTarget,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     AppInfoWithLiveness appInfo = appView.appInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index cd5b8b2..9ba2beb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -368,7 +368,7 @@
       return target;
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView.appInfo().resolveMethodOnClass(target.getHolderType(), target).asSingleResolution();
     if (resolutionResult == null
         || resolutionResult
@@ -384,7 +384,7 @@
       return target;
     }
 
-    SingleResolutionResult newResolutionResult =
+    SingleResolutionResult<?> newResolutionResult =
         appView.appInfo().resolveMethodOnClass(receiverType, target).asSingleResolution();
     if (newResolutionResult == null
         || newResolutionResult
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index 493b052..be64b57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -50,7 +50,7 @@
   @Override
   public boolean passesInliningConstraints(
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethod candidate,
       Reason reason,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
@@ -70,7 +70,7 @@
   public InlineResult computeInlining(
       IRCode code,
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethod singleTarget,
       ProgramMethod context,
       ClassInitializationAnalysis classInitializationAnalysis,
@@ -88,7 +88,7 @@
 
   private InlineAction computeForInvoke(
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     Inliner.InliningInfo info = invokesToInline.get(invoke);
     if (info == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 4b3bb6f..265d097 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -137,7 +137,7 @@
           AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
           AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
 
-          SingleResolutionResult resolutionResult =
+          SingleResolutionResult<?> resolutionResult =
               appInfoWithLiveness
                   .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
                   .asSingleResolution();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 577dbf2..4ca07c5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -960,7 +960,7 @@
           InvokeMethod invoke = current.asInvokeMethod();
           // TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget()!
           DexMethod invokedMethod = invoke.getInvokedMethod();
-          SingleResolutionResult resolutionResult =
+          SingleResolutionResult<?> resolutionResult =
               appView
                   .appInfo()
                   .resolveMethod(invokedMethod, invoke.getInterfaceBit())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
index ddc31c2..b484dda 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -26,7 +26,7 @@
 
   boolean passesInliningConstraints(
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethod candidate,
       Reason reason,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
@@ -34,7 +34,7 @@
   InlineResult computeInlining(
       IRCode code,
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethod singleTarget,
       ProgramMethod context,
       ClassInitializationAnalysis classInitializationAnalysis,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 7e494f3..09b614e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -289,7 +289,7 @@
       return;
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).asSingleResolution();
     if (resolutionResult == null) {
       return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
index 8312643..1a35898 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
@@ -88,7 +88,7 @@
         continue;
       }
 
-      SingleResolutionResult resolutionResult =
+      SingleResolutionResult<?> resolutionResult =
           appView
               .appInfo()
               .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 974bedd..0b876a7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -291,7 +291,7 @@
 
         if (user.isInvokeMethod()) {
           InvokeMethod invoke = user.asInvokeMethod();
-          SingleResolutionResult resolutionResult =
+          SingleResolutionResult<?> resolutionResult =
               appView
                   .appInfo()
                   .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
@@ -1071,7 +1071,7 @@
   //
   private boolean isEligibleDirectMethodCall(
       InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethod singleTarget,
       Supplier<InliningOracle> defaultOracle,
       Set<Instruction> indirectUsers) {
@@ -1175,7 +1175,7 @@
     if (!usage.isBottom()) {
       NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty();
       for (DexMethod invokedMethod : nonEmptyUsage.getMethodCallsWithParameterAsReceiver()) {
-        SingleResolutionResult resolutionResult =
+        SingleResolutionResult<?> resolutionResult =
             appView.appInfo().resolveMethodOn(eligibleClass, invokedMethod).asSingleResolution();
         if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
           return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index 0e4a8dd..ebad797 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -247,7 +247,7 @@
       return state.abandonClassInliningInCurrentContexts(receiverRoot);
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView
             .appInfo()
             .resolveMethodOnClassHolder(invoke.getInvokedMethod())
@@ -286,7 +286,7 @@
       return state;
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView
             .appInfo()
             .resolveMethodOnInterfaceHolder(invoke.getInvokedMethod())
@@ -302,7 +302,7 @@
   private ParameterUsages analyzeInvokeStatic(InvokeStatic invoke, NonEmptyParameterUsages state) {
     // We generally don't class inline instances that escape through invoke-static calls, but we
     // make an exception for calls to Objects.requireNonNull().
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView
             .appInfo()
             .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
@@ -328,7 +328,7 @@
       return state;
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView
             .appInfo()
             .resolveMethodOnClassHolder(invoke.getInvokedMethod())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
index 34514e3..9d4ae82 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
@@ -48,7 +48,7 @@
       // Check if this is a program class with a toString() method that does not have side effects.
       DexClass clazz = appInfo.definitionFor(classType);
       if (clazz != null && clazz.isEffectivelyFinal(appView)) {
-        SingleResolutionResult resolutionResult =
+        SingleResolutionResult<?> resolutionResult =
             appInfo.resolveMethodOn(clazz, toStringMethodReference).asSingleResolution();
         if (resolutionResult != null
             && !resolutionResult.getResolvedMethod().getOptimizationInfo().mayHaveSideEffects()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
index b28520c..d207ac7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
@@ -18,7 +18,7 @@
 
   public static AssumeInfo lookupAssumeInfo(
       AppView<AppInfoWithLiveness> appView,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       DexClassAndMethod singleTarget) {
     AssumeInfo resolutionLookup = lookupAssumeInfo(appView, resolutionResult.getResolutionPair());
     if (resolutionLookup == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
index 93f6242..ef2f42d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -173,7 +173,7 @@
       return null;
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView.appInfo().resolveMethodOn(superClass, method.getReference()).asSingleResolution();
     if (resolutionResult == null) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index e13e860..2e373e0 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -62,7 +62,7 @@
 
   private DexMethod validMemberRebindingTargetForNonProgramMethod(
       DexClassAndMethod resolvedMethod,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethodSet contexts,
       Type invokeType,
       DexMethod original) {
@@ -95,7 +95,7 @@
     }
 
     LibraryMethod eligibleLibraryMethod = null;
-    SingleResolutionResult currentResolutionResult = resolutionResult;
+    SingleResolutionResult<?> currentResolutionResult = resolutionResult;
     while (currentResolutionResult != null) {
       DexClassAndMethod currentResolvedMethod = currentResolutionResult.getResolutionPair();
       if (canRebindDirectlyToLibraryMethod(
@@ -138,7 +138,7 @@
 
   private boolean canRebindDirectlyToLibraryMethod(
       DexClassAndMethod resolvedMethod,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethodSet contexts,
       Type invokeType) {
     // TODO(b/194422791): It could potentially be that `original.holder` is not a subtype of
@@ -154,7 +154,7 @@
 
   private boolean isAccessibleInAllContexts(
       DexClassAndMethod resolvedMethod,
-      SingleResolutionResult resolutionResult,
+      SingleResolutionResult<?> resolutionResult,
       ProgramMethodSet contexts) {
     if (resolvedMethod.getHolder().isPublic() && resolvedMethod.getDefinition().isPublic()) {
       return true;
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index ebf61af..b819518 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -215,7 +215,7 @@
       }
       DexClass holder = appView.contextIndependentDefinitionFor(reference.getHolderType());
       if (holder != null) {
-        SingleResolutionResult resolutionResult =
+        SingleResolutionResult<?> resolutionResult =
             appView.appInfo().resolveMethodOn(holder, reference).asSingleResolution();
         if (resolutionResult != null && resolutionResult.getResolvedHolder() != holder) {
           recordNonReboundMethodAccess(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
index 1363a68..38c08c6 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -215,7 +215,7 @@
       if (holder == null) {
         return;
       }
-      SingleResolutionResult resolutionResult =
+      SingleResolutionResult<?> resolutionResult =
           appInfo.resolveMethodOn(holder, method).asSingleResolution();
       if (resolutionResult == null) {
         return;
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
index 745cf99..cbb7761 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
@@ -21,7 +21,7 @@
     if (clazz == null) {
       return false;
     }
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView.appInfo().resolveMethodOn(clazz, method).asSingleResolution();
     return resolutionResult != null
         && resolutionResult.getResolvedHolder().getType() != method.getHolderType();
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 ba4b05a..150f7ba 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
@@ -163,7 +163,7 @@
       return;
     }
 
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).asSingleResolution();
     if (resolutionResult == null) {
       // Nothing to propagate; the invoke instruction fails.
@@ -540,7 +540,7 @@
     // If the bootstrap method is program declared it will be called. The call is with runtime
     // provided arguments so ensure that the argument information is unknown.
     DexMethodHandle bootstrapMethod = invoke.getCallSite().bootstrapMethod;
-    SingleResolutionResult resolution =
+    SingleResolutionResult<?> resolution =
         appView
             .appInfo()
             .resolveMethod(bootstrapMethod.asMethod(), bootstrapMethod.isInterface)
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index fdf81ab..f7f7d06 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -187,7 +187,7 @@
     }
 
     private void registerInvokeMethod(DexMethod method) {
-      SingleResolutionResult resolutionResult =
+      SingleResolutionResult<?> resolutionResult =
           appView.appInfo().unsafeResolveMethodDueToDexFormat(method).asSingleResolution();
       if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
         return;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
index 787ae13..59d8ba7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
@@ -102,7 +102,7 @@
 
               inactiveMethodStates.forEach(
                   (signature, methodState) -> {
-                    SingleResolutionResult resolutionResult =
+                    SingleResolutionResult<?> resolutionResult =
                         appView.appInfo().resolveMethodOn(clazz, signature).asSingleResolution();
 
                     // Find the first virtual method in the super class hierarchy.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
index 90e9ead..87f26e7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
@@ -124,7 +124,7 @@
             }
 
             // Do not reprocess the method if the invoke resolves to a library method.
-            SingleResolutionResult resolutionResult =
+            SingleResolutionResult<?> resolutionResult =
                 appView
                     .appInfo()
                     .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
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 eac02d2..34425b3 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1387,7 +1387,7 @@
           singleTargetLookupCache.getCachedItem(refinedReceiverType, method);
       return cachedItem;
     }
-    SingleResolutionResult resolution =
+    SingleResolutionResult<?> resolution =
         resolveMethodOn(initialResolutionHolder, method).asSingleResolution();
     if (resolution == null
         || resolution.isAccessibleForVirtualDispatchFrom(context.getHolder(), this).isFalse()) {
@@ -1460,7 +1460,7 @@
   private DexEncodedMethod getMethodTargetFromExactRuntimeInformation(
       DexType refinedReceiverType,
       ClassTypeElement receiverLowerBoundType,
-      SingleResolutionResult resolution,
+      SingleResolutionResult<?> resolution,
       DexClass refinedReceiverClass) {
     // If the lower-bound on the receiver type is the same as the upper-bound, then we have exact
     // runtime type information. In this case, the invoke will dispatch to the resolution result
@@ -1555,6 +1555,7 @@
   public SubtypingInfo computeSubtypingInfo() {
     return SubtypingInfo.create(this);
   }
+
   /** Predicate on types that *must* never be merged horizontally. */
   public boolean isNoHorizontalClassMergingOfType(DexType type) {
     return noClassMerging.contains(type) || noHorizontalClassMerging.contains(type);
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 a26f167..98562f9 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1140,7 +1140,7 @@
   }
 
   private void disableClosedWorldReasoning(DexMethod reference, ProgramMethod context) {
-    SingleResolutionResult resolutionResult =
+    SingleResolutionResult<?> resolutionResult =
         resolveMethod(reference, context, KeepReason.methodHandleReferencedIn(context));
     if (resolutionResult != null && resolutionResult.getResolvedHolder().isProgramClass()) {
       applyMinimumKeepInfoWhenLiveOrTargeted(
@@ -2296,7 +2296,7 @@
     return fieldResolutionResult;
   }
 
-  private SingleResolutionResult resolveMethod(
+  private SingleResolutionResult<?> resolveMethod(
       DexMethod method, ProgramDefinition context, KeepReason reason) {
     // Record the references in case they are not program types.
     MethodResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
@@ -2310,7 +2310,7 @@
     return resolutionResult.asSingleResolution();
   }
 
-  private SingleResolutionResult resolveMethod(
+  private SingleResolutionResult<?> resolveMethod(
       DexMethod method, ProgramDefinition context, KeepReason reason, boolean interfaceInvoke) {
     // Record the references in case they are not program types.
     MethodResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
@@ -2328,7 +2328,7 @@
 
   private void handleInvokeOfStaticTarget(
       DexMethod reference, ProgramDefinition context, KeepReason reason) {
-    SingleResolutionResult resolution = resolveMethod(reference, context, reason);
+    SingleResolutionResult<?> resolution = resolveMethod(reference, context, reason);
     if (resolution == null || resolution.getResolvedHolder().isNotProgramClass()) {
       return;
     }
@@ -2792,7 +2792,7 @@
     getReachableVirtualTargets(currentClass)
         .forEach(
             (resolutionSearchKey, contexts) -> {
-              SingleResolutionResult singleResolution =
+              SingleResolutionResult<?> singleResolution =
                   appInfo
                       .resolveMethod(resolutionSearchKey.method, resolutionSearchKey.isInterface)
                       .asSingleResolution();
@@ -3240,7 +3240,7 @@
       return;
     }
 
-    SingleResolutionResult resolution = resolveMethod(method, context, reason, interfaceInvoke);
+    SingleResolutionResult<?> resolution = resolveMethod(method, context, reason, interfaceInvoke);
     if (resolution == null) {
       return;
     }
@@ -3386,7 +3386,7 @@
   // Package protected due to entry point from worklist.
   void markSuperMethodAsReachable(DexMethod reference, ProgramMethod from) {
     KeepReason reason = KeepReason.targetedBySuperFrom(from);
-    SingleResolutionResult resolution = resolveMethod(reference, from, reason);
+    SingleResolutionResult<?> resolution = resolveMethod(reference, from, reason);
     if (resolution == null) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 484cd3a..3b96973 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -598,7 +598,7 @@
       }
 
       private void tryAndKeepMethodOnClass(DexClassAndMethod method, ProguardMemberRule rule) {
-        SingleResolutionResult resolutionResult =
+        SingleResolutionResult<?> resolutionResult =
             appView
                 .appInfo()
                 .resolveMethodOn(originalClazz, method.getReference())
diff --git a/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java b/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java
index 7407b59..7dc7b3e 100644
--- a/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java
+++ b/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java
@@ -108,7 +108,7 @@
         }
 
         for (DexMethodSignature interfaceMethod : interfaceMethodsOfInterestForClass) {
-          SingleResolutionResult resolutionResult =
+          SingleResolutionResult<?> resolutionResult =
               appView
                   .appInfo()
                   .resolveMethodOnClass(implementer, interfaceMethod)