Visit library interfaces if super interfaces of classpath interfaces

Bug: 181887416
Change-Id: Ifbce94da3702116fa0911b100037befb6c2aa93c
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index b886f2b..50d1652 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -48,7 +48,7 @@
     SubtypingInfo subtypingInfo = appView.appInfo().computeSubtypingInfo();
     timing.begin("ComputeInterfaces");
     Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.slowCompareTo(b.type));
-    interfaces.addAll(appView.appInfo().computeReachableInterfaces());
+    appView.appInfo().forEachReachableInterface(interfaces::add);
     timing.end();
     timing.begin("MinifyClasses");
     ClassNameMinifier classNameMinifier =
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index fab1147..770d32e 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.naming;
 
+import static com.android.tools.r8.utils.IterableUtils.fromMethod;
+
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -47,7 +49,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.BiPredicate;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -96,19 +97,17 @@
 
     timing.begin("MappingInterfaces");
     Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.slowCompareTo(b.type));
-    Consumer<DexClass> consumer =
-        dexClass -> {
-          if (dexClass.isInterface()) {
-            // Only visit top level interfaces because computeMapping will visit the hierarchy.
-            if (dexClass.interfaces.isEmpty()) {
-              computeMapping(dexClass.type, nonPrivateMembers, notMappedReferences, subtypingInfo);
-            }
-            interfaces.add(dexClass);
-          }
-        };
     // For union-find of interface methods we also need to add the library types above live types.
-    appInfo.forEachTypeInHierarchyOfLiveProgramClasses(consumer);
-    appInfo.forEachReferencedClasspathClass(consumer::accept);
+    appInfo.forEachReachableInterface(
+        iFace -> {
+          assert iFace.isInterface();
+          interfaces.add(iFace);
+          if (iFace.interfaces.isEmpty()) {
+            computeMapping(iFace.type, nonPrivateMembers, notMappedReferences, subtypingInfo);
+          }
+        },
+        fromMethod(appInfo::forEachReferencedClasspathClass, DexClass::getType));
+
     assert nonPrivateMembers.isEmpty();
     timing.end();
 
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 4edec01..93dfaa9 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -10,7 +10,6 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -73,9 +72,7 @@
 
     // Phase 2: Visit classes and promote class/member to public if possible.
     timing.begin("Phase 2: promoteToPublic");
-    for (DexClass iface : appView.appInfo().computeReachableInterfaces()) {
-      publicizeType(iface.type);
-    }
+    appView.appInfo().forEachReachableInterface(clazz -> publicizeType(clazz.getType()));
     publicizeType(appView.dexItemFactory().objectType);
     timing.end();
 
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 fb1eeeb..f78dacd 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -58,6 +58,7 @@
 import com.android.tools.r8.utils.Visibility;
 import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
@@ -614,9 +615,14 @@
     return clazz == null || !clazz.isProgramClass();
   }
 
-  public Collection<DexClass> computeReachableInterfaces() {
-    Set<DexClass> interfaces = Sets.newIdentityHashSet();
+  public void forEachReachableInterface(Consumer<DexClass> consumer) {
+    forEachReachableInterface(consumer, ImmutableList.of());
+  }
+
+  public void forEachReachableInterface(
+      Consumer<DexClass> consumer, Iterable<DexType> additionalPaths) {
     WorkList<DexType> worklist = WorkList.newIdentityWorkList();
+    worklist.addIfNotSeen(additionalPaths);
     worklist.addIfNotSeen(objectAllocationInfoCollection.getInstantiatedLambdaInterfaces());
     for (DexProgramClass clazz : classes()) {
       worklist.addIfNotSeen(clazz.type);
@@ -628,16 +634,11 @@
         continue;
       }
       if (definition.isInterface()) {
-        interfaces.add(definition);
+        consumer.accept(definition);
       }
-      if (definition.superType != null) {
-        worklist.addIfNotSeen(definition.superType);
-      }
-      worklist.addIfNotSeen(definition.interfaces.values);
+      definition.forEachImmediateSupertype(worklist::addIfNotSeen);
     }
-    return interfaces;
   }
-
   /**
    * Resolve the methods implemented by the lambda expression that created the {@code callSite}.
    *
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index af3208f..8c652f5 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -7,6 +7,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 public class IterableUtils {
@@ -49,4 +51,10 @@
     iterable.forEach(result::add);
     return result;
   }
+
+  public static <T, R> Iterable<R> fromMethod(Consumer<Consumer<T>> method, Function<T, R> mapper) {
+    List<R> ts = new ArrayList<>();
+    method.accept(t -> ts.add(mapper.apply(t)));
+    return ts;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index 8ee6e30..12afc13 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -27,7 +27,7 @@
   }
 
   public static <T> WorkList<T> newIdentityWorkList() {
-    return new WorkList<T>(EqualityTest.IDENTITY);
+    return new WorkList<>(EqualityTest.IDENTITY);
   }
 
   public static <T> WorkList<T> newIdentityWorkList(T item) {
@@ -54,7 +54,7 @@
     items.forEach(workingList::addLast);
   }
 
-  public void addIfNotSeen(Iterable<T> items) {
+  public void addIfNotSeen(Iterable<? extends T> items) {
     items.forEach(this::addIfNotSeen);
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingClassPathInterfaceInheritTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingClassPathInterfaceInheritTest.java
index 3ec9e8f..5270eab 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingClassPathInterfaceInheritTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingClassPathInterfaceInheritTest.java
@@ -61,10 +61,9 @@
         .addRunClasspathClasses(LibI.class)
         .addRunClasspathFiles(libraryJar)
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/181887416): Should not throw an error when minifyLibrary == true.
         .applyIf(
             minifyLibrary,
-            r -> r.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
+            r -> r.assertSuccessWithOutputLines("a.a"),
             r -> r.assertSuccessWithOutputLines(ClassPathI.class.getTypeName()));
   }