Add tests for constructor merging
Bug: 189296638
Change-Id: I45f89cce32651b9304d0bd7b58192ed1aa8462fb
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAfterUnusedArgumentRemovalMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAfterUnusedArgumentRemovalMergingTest.java
new file mode 100644
index 0000000..5d9722b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAfterUnusedArgumentRemovalMergingTest.java
@@ -0,0 +1,120 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithClassIdAfterUnusedArgumentRemovalMergingTest
+ extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EquivalentConstructorsWithClassIdAfterUnusedArgumentRemovalMergingTest(
+ TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/189296638): Should be 1.
+ assertEquals(
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("C", "D");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A(new Object(), new C()));
+ System.out.println(new B(new Object(), new D()));
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ private final C c;
+
+ A(Object unused, C c) {
+ this.c = c;
+ }
+
+ @Override
+ public String toString() {
+ return c.toString();
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ private final D d;
+
+ B(Object unused, D d) {
+ this.d = d;
+ }
+
+ @Override
+ public String toString() {
+ return d.toString();
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class C {
+
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class D {
+
+ @Override
+ public String toString() {
+ return "D";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest.java
new file mode 100644
index 0000000..e70cd5a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest.java
@@ -0,0 +1,125 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest
+ extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest(
+ TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/189296638): Enable constructor merging by changing the constructor
+ // arguments.
+ assertEquals(
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("CD", "CD");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A(new C(), new D()));
+ System.out.println(new B(new D(), new C()));
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ private final C c;
+ private final D d;
+
+ A(C c, D d) {
+ this.c = c;
+ this.d = d;
+ }
+
+ @Override
+ public String toString() {
+ return c.toString() + d.toString();
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ private final C c;
+ private final D d;
+
+ B(D d, C c) {
+ this.d = d;
+ this.c = c;
+ }
+
+ @Override
+ public String toString() {
+ return c.toString() + d.toString();
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class C {
+
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class D {
+
+ @Override
+ public String toString() {
+ return "D";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest.java
new file mode 100644
index 0000000..a5d7e26
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest.java
@@ -0,0 +1,126 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest
+ extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest(
+ TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ // TODO(b/189296638): Should be 1, but requires changing the order of arguments at
+ // constructor call sites.
+ assertEquals(
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("C0", "D1");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A(0, new C()));
+ System.out.println(new B(new D(), 1));
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ private final C c;
+ private final int i;
+
+ A(int i, C c) {
+ this.c = c;
+ this.i = i;
+ }
+
+ @Override
+ public String toString() {
+ return c.toString() + i;
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ private final D d;
+ private final int i;
+
+ B(D d, int i) {
+ this.d = d;
+ this.i = i;
+ }
+
+ @Override
+ public String toString() {
+ return d.toString() + i;
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class C {
+
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class D {
+
+ @Override
+ public String toString() {
+ return "D";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentAssignmentOrderMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentAssignmentOrderMergingTest.java
new file mode 100644
index 0000000..bfce2d6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentAssignmentOrderMergingTest.java
@@ -0,0 +1,125 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithClassIdAndDifferentAssignmentOrderMergingTest
+ extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EquivalentConstructorsWithClassIdAndDifferentAssignmentOrderMergingTest(
+ TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/189296638): Should be 1.
+ assertEquals(
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("CC", "DD");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A(new C(), new C()));
+ System.out.println(new B(new D(), new D()));
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ private final C c;
+ private final C c2;
+
+ A(C c, C c2) {
+ this.c = c;
+ this.c2 = c2;
+ }
+
+ @Override
+ public String toString() {
+ return c.toString() + c2.toString();
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ private final D d;
+ private final D d2;
+
+ B(D d, D d2) {
+ // d2 intentionally assigned before d.
+ this.d2 = d2;
+ this.d = d;
+ }
+
+ @Override
+ public String toString() {
+ return d.toString() + d2.toString();
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class C {
+
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class D {
+
+ @Override
+ public String toString() {
+ return "D";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
new file mode 100644
index 0000000..5e144b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
@@ -0,0 +1,150 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import org.junit.AssumptionViolatedException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithClassIdAndExtraNullsMergingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EquivalentConstructorsWithClassIdAndExtraNullsMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/189296638): Should be 2.
+ assertEquals(
+ 3, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+
+ ClassSubject nullArgumentClassSubject =
+ inspector.allClasses().stream()
+ .filter(
+ clazz ->
+ SyntheticItemsTestUtils.isHorizontalInitializerTypeArgument(
+ clazz.getOriginalReference()))
+ .findFirst()
+ // TODO(b/189296638): Should throw RuntimeException.
+ .orElseThrow(
+ () ->
+ new AssumptionViolatedException(
+ "Expected Horizontal initializer type argument"));
+
+ assertThat(
+ aClassSubject.method("void", "<init>", "java.lang.Object", "int"), isPresent());
+ assertThat(
+ aClassSubject.method(
+ "void",
+ "<init>",
+ "java.lang.Object",
+ "int",
+ nullArgumentClassSubject.getFinalName()),
+ isPresent());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("C", "42", "C", "D");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A(new C()));
+ System.out.println(new A(new C(), 42));
+ System.out.println(new B(new D()));
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ private final Object c;
+
+ A(C c) {
+ this.c = c;
+ }
+
+ A(Object c, int i) {
+ this.c = c;
+ System.out.println(i);
+ }
+
+ @Override
+ public String toString() {
+ return c.toString();
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ private final D d;
+
+ B(D d) {
+ this.d = d;
+ }
+
+ @Override
+ public String toString() {
+ return d.toString();
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class C {
+
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class D {
+
+ @Override
+ public String toString() {
+ return "D";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdMergingTest.java
new file mode 100644
index 0000000..59756bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdMergingTest.java
@@ -0,0 +1,118 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithClassIdMergingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EquivalentConstructorsWithClassIdMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/189296638): Should be 1.
+ assertEquals(
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("C", "D");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A(new C()));
+ System.out.println(new B(new D()));
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ private final C c;
+
+ A(C c) {
+ this.c = c;
+ }
+
+ @Override
+ public String toString() {
+ return c.toString();
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ private final D d;
+
+ B(D d) {
+ this.d = d;
+ }
+
+ @Override
+ public String toString() {
+ return d.toString();
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class C {
+
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class D {
+
+ @Override
+ public String toString() {
+ return "D";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithInterfaceValueToParentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithInterfaceValueToParentTest.java
new file mode 100644
index 0000000..8785c85
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithInterfaceValueToParentTest.java
@@ -0,0 +1,115 @@
+// 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoUnusedInterfaceRemoval;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithInterfaceValueToParentTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public EquivalentConstructorsWithInterfaceValueToParentTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoUnusedInterfaceRemovalAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("K", "L");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A(new KImpl());
+ new B(new LImpl());
+ }
+ }
+
+ static class Parent {
+ Parent(J j) {
+ j.m();
+ }
+ }
+
+ @NeverClassInline
+ static class A extends Parent {
+ // When merging this initializer with B(L), it is important that we choose the signature AB(J)
+ // and not AB(I) or AB(Object), or the program will not verify.
+ A(K k) {
+ super(k);
+ }
+ }
+
+ @NeverClassInline
+ static class B extends Parent {
+ B(L l) {
+ super(l);
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class KImpl implements K {
+ @Override
+ public void m() {
+ System.out.println("K");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class LImpl implements L {
+ @Override
+ public void m() {
+ System.out.println("L");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface I {
+ void m();
+ }
+
+ @NoHorizontalClassMerging
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface J extends I {}
+
+ @NoHorizontalClassMerging
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface K extends J {}
+
+ @NoHorizontalClassMerging
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface L extends J {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithoutClassIdAfterUnusedArgumentRemovalMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithoutClassIdAfterUnusedArgumentRemovalMergingTest.java
new file mode 100644
index 0000000..d429057
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithoutClassIdAfterUnusedArgumentRemovalMergingTest.java
@@ -0,0 +1,122 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithoutClassIdAfterUnusedArgumentRemovalMergingTest
+ extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EquivalentConstructorsWithoutClassIdAfterUnusedArgumentRemovalMergingTest(
+ TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/189296638): Should be 1.
+ assertEquals(
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("C", "D");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A(new Object(), new C()).foo());
+ System.out.println(new B(new Object(), new D()).bar());
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ private final C c;
+
+ A(Object unused, C c) {
+ this.c = c;
+ }
+
+ @NeverInline
+ public String foo() {
+ return c.toString();
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ private final D d;
+
+ B(Object unused, D d) {
+ this.d = d;
+ }
+
+ @NeverInline
+ public String bar() {
+ return d.toString();
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class C {
+
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class D {
+
+ @Override
+ public String toString() {
+ return "D";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithoutClassIdMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithoutClassIdMergingTest.java
new file mode 100644
index 0000000..a8b85e0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithoutClassIdMergingTest.java
@@ -0,0 +1,120 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EquivalentConstructorsWithoutClassIdMergingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EquivalentConstructorsWithoutClassIdMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/189296638): Should be 1.
+ assertEquals(
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("C", "D");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A(new C()).foo());
+ System.out.println(new B(new D()).bar());
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ private final C c;
+
+ A(C c) {
+ this.c = c;
+ }
+
+ @NeverInline
+ public String foo() {
+ return c.toString();
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ private final D d;
+
+ B(D d) {
+ this.d = d;
+ }
+
+ @NeverInline
+ public String bar() {
+ return d.toString();
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class C {
+
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class D {
+
+ @Override
+ public String toString() {
+ return "D";
+ }
+ }
+}