Take library class over program class when computing dispatch

Bug: b/234833465
Change-Id: I66e533d06b57eb5418eb25666b4cae1f8a893f75
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 12baacf..17ba862 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
@@ -14,10 +14,12 @@
 
   DexClass toSingleClassWithProgramOverLibrary();
 
+  DexClass toSingleClassWithLibraryOverProgram();
+
   // The alternative class is:
   // - the other class than the single class if the resolution resolves into multiple classes,
   // - null if the resolution resolves into a single class.
-  DexClass toAlternativeClassWithProgramOverLibrary();
+  DexClass toAlternativeClass();
 
   void forEachClassResolutionResult(Consumer<DexClass> consumer);
 
@@ -95,7 +97,12 @@
     }
 
     @Override
-    public DexClass toAlternativeClassWithProgramOverLibrary() {
+    public DexClass toSingleClassWithLibraryOverProgram() {
+      return null;
+    }
+
+    @Override
+    public DexClass toAlternativeClass() {
       return null;
     }
 
@@ -147,7 +154,12 @@
     }
 
     @Override
-    public DexClass toAlternativeClassWithProgramOverLibrary() {
+    public DexClass toSingleClassWithLibraryOverProgram() {
+      return libraryClass;
+    }
+
+    @Override
+    public DexClass toAlternativeClass() {
       return libraryClass;
     }
   }
@@ -166,7 +178,12 @@
     }
 
     @Override
-    public DexClass toAlternativeClassWithProgramOverLibrary() {
+    public DexClass toSingleClassWithLibraryOverProgram() {
+      return libraryClass;
+    }
+
+    @Override
+    public DexClass toAlternativeClass() {
       return programOrClasspathClass;
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 717ac38d..265fc65 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -156,7 +156,12 @@
   }
 
   @Override
-  public DexClass toAlternativeClassWithProgramOverLibrary() {
+  public DexClass toSingleClassWithLibraryOverProgram() {
+    return this;
+  }
+
+  @Override
+  public DexClass toAlternativeClass() {
     return null;
   }
 
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 8730c35..5b43e8d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -344,6 +344,15 @@
       return null;
     }
 
+    private static DexClass definitionForHelper(AppInfoWithClassHierarchy appInfo, DexType type) {
+      if (type == null) {
+        return null;
+      }
+      return appInfo
+          .contextIndependentDefinitionForWithResolutionResult(type)
+          .toSingleClassWithLibraryOverProgram();
+    }
+
     /**
      * Lookup the target of an invoke-super.
      *
@@ -447,7 +456,7 @@
           && !symbolicReference.isInterface()
           && isSuperclass.test(symbolicReference, context)) {
         // If reference is a super type of the context then search starts at the immediate super.
-        initialType = context.superType == null ? null : appInfo.definitionFor(context.superType);
+        initialType = definitionForHelper(appInfo, context.superType);
       } else {
         // Otherwise it starts at the reference itself.
         initialType = symbolicReference;
@@ -465,7 +474,7 @@
         if (target != null) {
           break;
         }
-        current = current.superType == null ? null : appInfo.definitionFor(current.superType);
+        current = definitionForHelper(appInfo, current.superType);
       }
       // 4. Otherwise, it is the single maximally specific method:
       if (target == null) {
@@ -698,7 +707,7 @@
         Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
       if (lambdaInstance.getMainMethod().match(resolvedMethod)) {
         DexMethod methodReference = lambdaInstance.implHandle.asMethod();
-        DexClass holder = appInfo.definitionForHolder(methodReference);
+        DexClass holder = definitionForHelper(appInfo, methodReference.getHolderType());
         DexClassAndMethod method = methodReference.lookupMemberOnClass(holder);
         if (method == null) {
           // The targeted method might not exist, eg, Throwable.addSuppressed in an old library.
@@ -743,7 +752,7 @@
           if (current.type == overrideTarget.getHolderType()) {
             return null;
           }
-          current = current.superType == null ? null : appInfo.definitionFor(current.superType);
+          current = definitionForHelper(appInfo, current.superType);
           continue;
         }
         DexClassAndMethod target = DexClassAndMethod.create(current, candidate);
@@ -817,11 +826,11 @@
     }
 
     private static DexClassAndMethod findWideningOverride(
-        DexClassAndMethod resolvedMethod, DexClass clazz, AppInfoWithClassHierarchy appView) {
+        DexClassAndMethod resolvedMethod, DexClass clazz, AppInfoWithClassHierarchy appInfo) {
       // Otherwise, lookup to first override that is distinct from resolvedMethod.
       assert resolvedMethod.getDefinition().accessFlags.isPackagePrivate();
       while (clazz.superType != null) {
-        clazz = appView.definitionFor(clazz.superType);
+        clazz = definitionForHelper(appInfo, clazz.superType);
         if (clazz == null) {
           return resolvedMethod;
         }
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 bb1e898..689a3f0 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -692,7 +692,7 @@
         internalDefinitionFor(
             programClass.type, programClass, this::recordNonProgramClass, this::reportMissingClass);
     assert classResolutionResult.hasClassResolutionResult();
-    DexClass alternativeClass = classResolutionResult.toAlternativeClassWithProgramOverLibrary();
+    DexClass alternativeClass = classResolutionResult.toAlternativeClass();
     assert alternativeClass == null || alternativeClass.isLibraryClass();
     return alternativeClass != null;
   }
@@ -2562,9 +2562,7 @@
       return;
     }
     DexClass alternativeResolutionResult =
-        appInfo()
-            .contextIndependentDefinitionForWithResolutionResult(type)
-            .toAlternativeClassWithProgramOverLibrary();
+        appInfo().contextIndependentDefinitionForWithResolutionResult(type).toAlternativeClass();
     if (alternativeResolutionResult != null && alternativeResolutionResult.isLibraryClass()) {
       // We are in a situation where a library class inherits from a library class, which has a
       // program class duplicated version for low API levels.