Reproduce NoSuchMethodError from method merging with absent method
Bug: b/231900726
Change-Id: Ia9cd3ff072202ff8d647d11b6b50f17cbd390df8
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest.java
new file mode 100644
index 0000000..a7d9bf1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest.java
@@ -0,0 +1,119 @@
+// Copyright (c) 2022, 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.classmerging.horizontal;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+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;
+
+/** Reproduction of b/231900726. */
+@RunWith(Parameterized.class)
+public class VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest extends TestBase {
+
+ @Parameter(0)
+ public Class<?> upperMergeTarget;
+
+ @Parameter(1)
+ public Class<?> lowerMergeTarget;
+
+ @Parameter(2)
+ public TestParameters parameters;
+
+ @Parameters(name = "{2}, upper merge target: {0}, lower merge target: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of(A.class, B.class),
+ ImmutableList.of(C.class, D.class),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ // Control the targets of the horizontal class merger.
+ .addOptionsModification(
+ options ->
+ options.testing.horizontalClassMergingTarget =
+ (appView, candidates, target) -> {
+ if (Iterables.any(
+ candidates,
+ candidate -> candidate.getTypeName().equals(A.class.getTypeName()))) {
+ return Iterables.find(
+ candidates,
+ candidate ->
+ candidate.getTypeName().equals(upperMergeTarget.getTypeName()));
+ }
+ if (Iterables.any(
+ candidates,
+ candidate -> candidate.getTypeName().equals(C.class.getTypeName()))) {
+ return Iterables.find(
+ candidates,
+ candidate ->
+ candidate.getTypeName().equals(lowerMergeTarget.getTypeName()));
+ }
+ return target;
+ })
+ // Verify that the targets are as expected.
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .applyIf(
+ upperMergeTarget == A.class,
+ i -> i.assertMergedInto(B.class, A.class),
+ i -> i.assertMergedInto(A.class, B.class))
+ .applyIf(
+ lowerMergeTarget == C.class,
+ i -> i.assertMergedInto(D.class, C.class),
+ i -> i.assertMergedInto(C.class, D.class))
+ .assertNoOtherClassesMerged())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().m();
+ new B().m();
+ new C().m();
+ new D().m();
+ }
+ }
+
+ static class A {
+
+ void m() {
+ System.out.println("A");
+ }
+ }
+
+ static class B {
+
+ void m() {
+ System.out.println("B");
+ }
+ }
+
+ static class C extends A {
+
+ @Override
+ void m() {
+ System.out.println("C");
+ }
+ }
+
+ static class D extends A {}
+}