Reproduce verifaction error from interface-invoke on Object

Bug: 199561570
Change-Id: I2d09cd430a16b334a30f9d605e051ea1593189b5
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
new file mode 100644
index 0000000..255e1fe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
@@ -0,0 +1,137 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.inliner;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InterfaceInvokeWithObjectReceiverInliningTest extends TestBase {
+
+  @Parameter(0)
+  public boolean enableInlining;
+
+  @Parameter(1)
+  public boolean enableVerticalClassMerging;
+
+  @Parameter(2)
+  public TestParameters parameters;
+
+  @Parameters(name = "{2}, inlining: {0}, vertical class merging: {1}")
+  public static List<Object[]> parameters() {
+    return buildParameters(
+        BooleanUtils.values(),
+        BooleanUtils.values(),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    assumeFalse(enableInlining);
+    assumeFalse(enableVerticalClassMerging);
+    testForRuntime(parameters)
+        .addProgramClasses(I.class, A.class)
+        .addProgramClassFileData(getTransformedMain())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("0");
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(I.class, A.class)
+        .addProgramClassFileData(getTransformedMain())
+        .addKeepMainRule(Main.class)
+        // Keep get() to prevent that we optimize it into having static return type A.
+        .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
+        .addInliningAnnotations()
+        .addNoVerticalClassMergingAnnotations()
+        .applyIf(!enableInlining, R8TestBuilder::enableInliningAnnotations)
+        .applyIf(
+            !enableVerticalClassMerging, R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/199561570): Should succeed with 0.
+        .applyIf(
+            (enableInlining
+                    && (!enableVerticalClassMerging
+                        || parameters.isDexRuntimeVersion(Version.V5_1_1)
+                        || parameters.isDexRuntimeVersion(Version.V6_0_1)))
+                || (!enableInlining && !enableVerticalClassMerging),
+            runResult -> runResult.assertSuccessWithOutputLines("0"),
+            runResult ->
+                runResult.assertFailureWithErrorThatMatches(
+                    anyOf(
+                        containsString(NoSuchFieldError.class.getTypeName()),
+                        containsString(VerifyError.class.getTypeName()))));
+  }
+
+  private static byte[] getTransformedMain() throws IOException {
+    return transformer(Main.class)
+        .transformMethodInsnInMethod(
+            "main",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              if (name.equals("get")) {
+                visitor.visitMethodInsn(opcode, owner, name, "(I)Ljava/lang/Object;", isInterface);
+              } else {
+                visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+              }
+            })
+        .setReturnType(MethodPredicate.onName("get"), Object.class.getTypeName())
+        .transform();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      // Transformed from `I get(int)` to `Object get(int)`.
+      get(args.length).m();
+    }
+
+    // @Keep
+    static /*Object*/ I get(int f) {
+      return new A(f);
+    }
+  }
+
+  @NoVerticalClassMerging
+  interface I {
+
+    void m();
+  }
+
+  static class A implements I {
+
+    int f;
+
+    A(int f) {
+      this.f = f;
+    }
+
+    @NeverInline
+    @Override
+    public void m() {
+      System.out.println(f);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 1b99096..c49899d 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -591,6 +591,25 @@
         });
   }
 
+  public ClassFileTransformer setReturnType(MethodPredicate predicate, String newReturnType) {
+    return addClassTransformer(
+        new ClassTransformer() {
+          @Override
+          public MethodVisitor visitMethod(
+              int access, String name, String descriptor, String signature, String[] exceptions) {
+            if (predicate.test(access, name, descriptor, signature, exceptions)) {
+              String oldDescriptorExcludingReturnType =
+                  descriptor.substring(0, descriptor.lastIndexOf(')') + 1);
+              String newDescriptor =
+                  oldDescriptorExcludingReturnType
+                      + DescriptorUtils.javaTypeToDescriptor(newReturnType);
+              return super.visitMethod(access, name, newDescriptor, signature, exceptions);
+            }
+            return super.visitMethod(access, name, descriptor, signature, exceptions);
+          }
+        });
+  }
+
   public ClassFileTransformer setGenericSignature(MethodPredicate predicate, String newSignature) {
     return addClassTransformer(
         new ClassTransformer() {