Merge "InterfaceProcessor should keep some bridge methods"
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index f4a76e6..c538e86 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -18,11 +19,15 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.origin.SynthesizedOrigin;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Deque;
+import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 // Default and static method interface desugaring processor for interfaces.
 //
@@ -78,7 +83,7 @@
       }
 
       // Remove bridge methods.
-      if (!virtual.accessFlags.isBridge()) {
+      if (interfaceMethodRemovalChangesApi(virtual, iface)) {
         remainingMethods.add(virtual);
       }
     }
@@ -180,6 +185,38 @@
     companionClasses.put(iface, companionClass);
   }
 
+  // Returns true if the given interface method must be kept on [iface] after moving its
+  // implementation to the companion class of [iface]. This is always the case for non-bridge
+  // methods. Bridge methods that does not override an implementation in a super-interface must
+  // also be kept (such a situation can happen if the vertical class merger merges two interfaces).
+  private boolean interfaceMethodRemovalChangesApi(DexEncodedMethod method, DexClass iface) {
+    if (method.accessFlags.isBridge()) {
+      Deque<DexType> worklist = new ArrayDeque<>();
+      Set<DexType> seenBefore = new HashSet<>();
+      if (iface.superType != null) {
+        worklist.add(iface.superType);
+      }
+      Collections.addAll(worklist, iface.interfaces.values);
+      while (!worklist.isEmpty()) {
+        DexType superType = worklist.pop();
+        if (!seenBefore.add(superType)) {
+          continue;
+        }
+        DexClass clazz = rewriter.findDefinitionFor(superType);
+        if (clazz != null) {
+          if (clazz.lookupVirtualMethod(method.method) != null) {
+            return false;
+          }
+          if (clazz.superType != null) {
+            worklist.add(clazz.superType);
+          }
+          Collections.addAll(worklist, clazz.interfaces.values);
+        }
+      }
+    }
+    return true;
+  }
+
   private boolean isStaticMethod(DexEncodedMethod method) {
     if (method.accessFlags.isNative()) {
       throw new Unimplemented("Native interface methods are not yet supported.");