Add a test for non-rebound fields with horizontal class merging

Change-Id: I204e4bd1dc29187c9a1de8becd00ba4826d484ce
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 0d6b606..a7bd6c1 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -461,6 +461,7 @@
   public void setHorizontallyMergedClasses(HorizontallyMergedClasses horizontallyMergedClasses) {
     assert this.horizontallyMergedClasses == null;
     this.horizontallyMergedClasses = horizontallyMergedClasses;
+    testing().horizontallyMergedClassesConsumer.accept(dexItemFactory(), horizontallyMergedClasses);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index ef58b3a..b1d0dbe 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -96,6 +96,7 @@
     Collection<Collection<DexProgramClass>> groups = policyExecutor.run(classes.values());
     // If there are no groups, then end horizontal class merging.
     if (groups.isEmpty()) {
+      appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
       return null;
     }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 01058e2..653e31a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -15,12 +15,17 @@
 import java.util.Set;
 
 public class HorizontallyMergedClasses implements MergedClasses {
+
   private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
 
   public HorizontallyMergedClasses(BidirectionalManyToOneMap<DexType, DexType> mergedClasses) {
     this.mergedClasses = mergedClasses;
   }
 
+  public static HorizontallyMergedClasses empty() {
+    return new HorizontallyMergedClasses(new BidirectionalManyToOneMap<>());
+  }
+
   public DexType getMergeTargetOrDefault(DexType type) {
     return mergedClasses.getOrDefault(type, type);
   }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 60153de..8bc1a55 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -39,6 +39,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.MethodProcessingId;
@@ -1230,6 +1231,9 @@
                 new DefaultRepackagingConfiguration(
                     appView.dexItemFactory(), appView.options().getProguardConfiguration());
 
+    public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
+        ConsumerUtils.emptyBiConsumer();
+
     public BiConsumer<DexItemFactory, HorizontallyMergedLambdaClasses>
         horizontallyMergedLambdaClassesConsumer = ConsumerUtils.emptyBiConsumer();
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
new file mode 100644
index 0000000..91dc3b1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, 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.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.testclasses.NonReboundFieldAccessOnMergedClassTestClasses;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NonReboundFieldAccessOnMergedClassTest extends HorizontalClassMergingTestBase {
+
+  public NonReboundFieldAccessOnMergedClassTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void test() throws Exception {
+    if (enableHorizontalClassMerging) {
+      thrown.expect(Throwable.class);
+    }
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .addHorizontallyMergedLambdaClassesInspector(
+            inspector -> {
+              if (enableHorizontalClassMerging) {
+                inspector.assertMerged(C.class, D.class);
+              }
+            })
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.print(new C("Hello").greeting);
+      System.out.println(new D(" world!").greeting);
+    }
+  }
+
+  @NeverClassInline
+  static class C extends NonReboundFieldAccessOnMergedClassTestClasses.B {
+
+    public C(String greeting) {
+      super(greeting);
+    }
+  }
+
+  @NeverClassInline
+  static class D extends NonReboundFieldAccessOnMergedClassTestClasses.B {
+
+    public D(String greeting) {
+      super(greeting);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
new file mode 100644
index 0000000..627c2d2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2020, 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.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.NonReboundFieldAccessOnMergedClassTest.C;
+import com.android.tools.r8.classmerging.horizontal.testclasses.NonReboundFieldAccessWithMergedTypeTestClasses;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NonReboundFieldAccessWithMergedTypeTest extends HorizontalClassMergingTestBase {
+
+  public NonReboundFieldAccessWithMergedTypeTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void test() throws Exception {
+    if (enableHorizontalClassMerging) {
+      thrown.expect(Throwable.class);
+    }
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .addHorizontallyMergedLambdaClassesInspector(
+            inspector -> {
+              if (enableHorizontalClassMerging) {
+                inspector.assertMerged(HelloGreeting.class, WorldGreeting.class);
+              }
+            })
+        .enableNeverClassInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.print(new C(new HelloGreeting()).greeting);
+      System.out.println(new WorldGreeting());
+    }
+  }
+
+  @NeverClassInline
+  static class C extends NonReboundFieldAccessWithMergedTypeTestClasses.B {
+
+    public C(HelloGreeting greeting) {
+      super(greeting);
+    }
+  }
+
+  public static class HelloGreeting {
+
+    @Override
+    public String toString() {
+      return "Hello";
+    }
+  }
+
+  public static class WorldGreeting {
+
+    @Override
+    public String toString() {
+      return " world!";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java
new file mode 100644
index 0000000..7bf88cf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, 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.testclasses;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+
+public class NonReboundFieldAccessOnMergedClassTestClasses {
+
+  @NoVerticalClassMerging
+  static class A {
+
+    public String greeting;
+
+    A(String greeting) {
+      this.greeting = greeting;
+    }
+  }
+
+  public static class B extends A {
+
+    public B(String greeting) {
+      super(greeting);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java
new file mode 100644
index 0000000..96e80b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2020, 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.testclasses;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.classmerging.horizontal.NonReboundFieldAccessWithMergedTypeTest.HelloGreeting;
+
+public class NonReboundFieldAccessWithMergedTypeTestClasses {
+
+  @NoHorizontalClassMerging
+  @NoVerticalClassMerging
+  static class A {
+
+    public HelloGreeting greeting;
+
+    A(HelloGreeting greeting) {
+      this.greeting = greeting;
+    }
+  }
+
+  @NoVerticalClassMerging
+  public static class B extends A {
+
+    public B(HelloGreeting greeting) {
+      super(greeting);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
new file mode 100644
index 0000000..80e8a5c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, 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.utils.codeinspector;
+
+import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
+
+public class HorizontallyMergedClassesInspector {
+
+  private final DexItemFactory dexItemFactory;
+  private final HorizontallyMergedClasses horizontallyMergedClasses;
+
+  public HorizontallyMergedClassesInspector(
+      DexItemFactory dexItemFactory, HorizontallyMergedClasses horizontallyMergedClasses) {
+    this.dexItemFactory = dexItemFactory;
+    this.horizontallyMergedClasses = horizontallyMergedClasses;
+  }
+
+  public HorizontallyMergedClassesInspector assertMerged(Class<?> clazz) {
+    assertTrue(horizontallyMergedClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+    return this;
+  }
+
+  public HorizontallyMergedClassesInspector assertMerged(Class<?>... classes) {
+    for (Class<?> clazz : classes) {
+      assertMerged(clazz);
+    }
+    return this;
+  }
+}