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/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index b75956e..d902890 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.naming;
 
 import static com.android.tools.r8.graph.DexApplication.classesWithDeterministicOrder;
+import static com.android.tools.r8.utils.IterableUtils.fromMethod;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -51,7 +52,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;
 
 /**
@@ -99,19 +99,17 @@
 
     timing.begin("MappingInterfaces");
     Set<DexClass> interfaces = new TreeSet<>(Comparator.comparing(DexClass::getType));
-    Consumer<DexClass> consumer =
-        clazz -> {
-          if (clazz.isInterface()) {
-            // Only visit top level interfaces because computeMapping will visit the hierarchy.
-            if (clazz.interfaces.isEmpty()) {
-              computeMapping(clazz.type, nonPrivateMembers, notMappedReferences, subtypingInfo);
-            }
-            interfaces.add(clazz);
-          }
-        };
     // 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/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index b186a52..c9db30c 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -59,6 +59,7 @@
 import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.android.tools.r8.utils.structural.Ordered;
+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;
@@ -662,7 +663,13 @@
   }
 
   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);
@@ -676,10 +683,7 @@
       if (definition.isInterface()) {
         consumer.accept(definition);
       }
-      if (definition.superType != null) {
-        worklist.addIfNotSeen(definition.superType);
-      }
-      worklist.addIfNotSeen(definition.interfaces.values);
+      definition.forEachImmediateSupertype(worklist::addIfNotSeen);
     }
   }
 
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 ce8396f..1354a33 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -12,6 +12,7 @@
 import java.util.List;
 import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -151,4 +152,10 @@
     }
     return !iterator.hasNext();
   }
+
+  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 e5fb33f..4279cb7 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) {
@@ -60,7 +60,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()));
   }