Disallow vertical class merging of invoke-special to default interface method

Change-Id: Ifc07308c7b739fa1500db698ef054aeea668657c
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 6e55f3c..8d38af0 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -442,6 +442,22 @@
       }
       return false;
     }
+
+    // If there is an invoke-special to a default interface method and we are not merging into an
+    // interface, then abort, since invoke-special to a virtual class method requires desugaring.
+    if (sourceClass.isInterface() && !targetClass.isInterface()) {
+      TraversalContinuation result =
+          sourceClass.traverseProgramMethods(
+              method -> {
+                boolean foundInvokeSpecialToDefaultLibraryMethod =
+                    method.registerCodeReferencesWithResult(
+                        new InvokeSpecialToDefaultLibraryMethodUseRegistry(appView, method));
+                return TraversalContinuation.breakIf(foundInvokeSpecialToDefaultLibraryMethod);
+              });
+      if (result.shouldBreak()) {
+        return false;
+      }
+    }
     return true;
   }
 
@@ -2113,6 +2129,62 @@
     }
   }
 
+  public static class InvokeSpecialToDefaultLibraryMethodUseRegistry
+      extends UseRegistryWithResult<Boolean, ProgramMethod> {
+
+    InvokeSpecialToDefaultLibraryMethodUseRegistry(
+        AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
+      super(appView, context, false);
+      assert context.getHolder().isInterface();
+    }
+
+    @Override
+    public void registerInvokeSpecial(DexMethod method) {
+      ProgramMethod context = getContext();
+      if (method.getHolderType() != context.getHolderType()) {
+        return;
+      }
+
+      DexEncodedMethod definition = context.getHolder().lookupMethod(method);
+      if (definition != null && definition.belongsToVirtualPool()) {
+        setResult(true);
+      }
+    }
+
+    @Override
+    public void registerInitClass(DexType type) {}
+
+    @Override
+    public void registerInvokeDirect(DexMethod method) {}
+
+    @Override
+    public void registerInvokeInterface(DexMethod method) {}
+
+    @Override
+    public void registerInvokeStatic(DexMethod method) {}
+
+    @Override
+    public void registerInvokeSuper(DexMethod method) {}
+
+    @Override
+    public void registerInvokeVirtual(DexMethod method) {}
+
+    @Override
+    public void registerInstanceFieldRead(DexField field) {}
+
+    @Override
+    public void registerInstanceFieldWrite(DexField field) {}
+
+    @Override
+    public void registerStaticFieldRead(DexField field) {}
+
+    @Override
+    public void registerStaticFieldWrite(DexField field) {}
+
+    @Override
+    public void registerTypeReference(DexType type) {}
+  }
+
   protected static class SynthesizedBridgeCode extends AbstractSynthesizedCode {
 
     private DexMethod method;
diff --git a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
index 2d8efe3..0e9d873 100644
--- a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
+++ b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
@@ -9,6 +9,10 @@
   CONTINUE,
   BREAK;
 
+  public static TraversalContinuation breakIf(boolean condition) {
+    return continueIf(!condition);
+  }
+
   public static TraversalContinuation continueIf(boolean condition) {
     return condition ? CONTINUE : BREAK;
   }