Extend argument propagator to instance initializers

Change-Id: I682f56382386e5acf1ab96508284d2f2e52b4778
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 83dfa77..0f2e98a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -46,6 +46,10 @@
         || result == null;
   }
 
+  public static Builder builder() {
+    return new Builder();
+  }
+
   @Override
   public int opcode() {
     return Opcodes.INVOKE_DIRECT;
@@ -210,4 +214,17 @@
 
     return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(appView, this);
   }
+
+  public static class Builder extends InvokeMethod.Builder<Builder, InvokeDirect> {
+
+    @Override
+    public InvokeDirect build() {
+      return amend(new InvokeDirect(method, outValue, arguments));
+    }
+
+    @Override
+    public Builder self() {
+      return this;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 8a97aef..d2a73ed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -56,13 +57,14 @@
 import com.android.tools.r8.utils.collections.ProgramMethodMap;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -241,13 +243,22 @@
     // Rewrite enum instantiations + remove static-puts to pruned fields.
     IRCode code = classInitializer.buildIR(appView);
     ListIterator<BasicBlock> blockIterator = code.listIterator();
-    Set<Instruction> instructionsToRemove = Sets.newIdentityHashSet();
+
+    // A mapping from instructions-to-be-removed from the IR to their lens-rewritten
+    // instruction (if any). If an instruction-to-be-removed has a lens-rewritten instruction, the
+    // lens-rewritten instruction must also be detached from the IR.
+    Map<Instruction, Optional<Instruction>> instructionsToRemove = new IdentityHashMap<>();
     while (blockIterator.hasNext()) {
       BasicBlock block = blockIterator.next();
       InstructionListIterator instructionIterator = block.listIterator(code);
       while (instructionIterator.hasNext()) {
         Instruction instruction = instructionIterator.next();
-        if (instructionsToRemove.remove(instruction)) {
+        if (instructionsToRemove.containsKey(instruction)) {
+          Optional<Instruction> rewrittenInstruction = instructionsToRemove.remove(instruction);
+          if (rewrittenInstruction.isPresent()) {
+            instructionIterator.replaceCurrentInstruction(rewrittenInstruction.get());
+            instructionIterator.previous();
+          }
           instructionIterator.removeOrReplaceByDebugLocalRead();
           continue;
         }
@@ -294,8 +305,47 @@
                 newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
             assert constructorInvoke != null;
 
+            DexMethod invokedMethod = constructorInvoke.getInvokedMethod();
+
+            // Rewrite the constructor invoke in case there are any removed arguments. This is
+            // required since we find the argument index of the ordinal value below, and use this to
+            // find the ordinal of the current enum instance.
+            MethodLookupResult lookupResult =
+                appView.graphLens().lookupInvokeDirect(invokedMethod, classInitializer);
+            if (lookupResult.getReference() != invokedMethod) {
+              List<Value> rewrittenArguments =
+                  new ArrayList<>(constructorInvoke.arguments().size());
+              for (int i = 0; i < constructorInvoke.arguments().size(); i++) {
+                Value argument = constructorInvoke.getArgument(i);
+                if (!lookupResult
+                    .getPrototypeChanges()
+                    .getArgumentInfoCollection()
+                    .isArgumentRemoved(i)) {
+                  rewrittenArguments.add(argument);
+                }
+              }
+              InvokeDirect originalConstructorInvoke = constructorInvoke;
+              constructorInvoke =
+                  InvokeDirect.builder()
+                      .setArguments(rewrittenArguments)
+                      .setMethod(lookupResult.getReference())
+                      .build();
+
+              // Record that the original constructor invoke has been rewritten into the new
+              // constructor invoke, and that these instructions need to be removed from the IR.
+              // Note that although the rewritten constructor invoke has not been inserted into the
+              // IR, the creation of it has added it as a user of each of the operands. To undo this
+              // we replace the original constructor invoke by the rewritten constructor invoke and
+              // then remove the rewritten constructor invoke from the IR.
+              instructionsToRemove.put(originalConstructorInvoke, Optional.of(constructorInvoke));
+            } else {
+              assert lookupResult.getPrototypeChanges().isEmpty();
+              // Record that the constructor invoke needs to be removed.
+              instructionsToRemove.put(constructorInvoke, Optional.empty());
+            }
+
             ProgramMethod constructor =
-                unboxedEnum.lookupProgramMethod(constructorInvoke.getInvokedMethod());
+                unboxedEnum.lookupProgramMethod(lookupResult.getReference());
             assert constructor != null;
 
             InstanceFieldInitializationInfo ordinalInitializationInfo =
@@ -334,8 +384,6 @@
                     code.createValue(
                         ClassTypeElement.create(
                             unboxedEnum.getType(), definitelyNotNull(), appView))));
-
-            instructionsToRemove.add(constructorInvoke);
           }
         } else if (instruction.isStaticPut()) {
           StaticPut staticPut = instruction.asStaticPut();
@@ -358,7 +406,13 @@
     if (!instructionsToRemove.isEmpty()) {
       InstructionListIterator instructionIterator = code.instructionListIterator();
       while (instructionIterator.hasNext()) {
-        if (instructionsToRemove.remove(instructionIterator.next())) {
+        Instruction instruction = instructionIterator.next();
+        if (instructionsToRemove.containsKey(instruction)) {
+          Optional<Instruction> rewrittenInstruction = instructionsToRemove.get(instruction);
+          if (rewrittenInstruction.isPresent()) {
+            instructionIterator.replaceCurrentInstruction(rewrittenInstruction.get());
+            instructionIterator.previous();
+          }
           instructionIterator.removeOrReplaceByDebugLocalRead();
         }
       }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 5f14c9d..90397d7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -54,7 +54,8 @@
   private final AppView<AppInfoWithLiveness> appView;
   private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
 
-  private final Map<DexClass, DexMethodSignatureSet> libraryMethods = new ConcurrentHashMap<>();
+  private final Map<DexClass, DexMethodSignatureSet> libraryVirtualMethods =
+      new ConcurrentHashMap<>();
 
   public ArgumentPropagatorProgramOptimizer(
       AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
@@ -88,25 +89,25 @@
     return graphLens;
   }
 
-  private DexMethodSignatureSet getOrComputeLibraryMethods(DexClass clazz) {
-    DexMethodSignatureSet libraryMethodsOnClass = libraryMethods.get(clazz);
+  private DexMethodSignatureSet getOrComputeLibraryVirtualMethods(DexClass clazz) {
+    DexMethodSignatureSet libraryMethodsOnClass = libraryVirtualMethods.get(clazz);
     if (libraryMethodsOnClass != null) {
       return libraryMethodsOnClass;
     }
-    return computeLibraryMethods(clazz);
+    return computeLibraryVirtualMethods(clazz);
   }
 
-  private DexMethodSignatureSet computeLibraryMethods(DexClass clazz) {
+  private DexMethodSignatureSet computeLibraryVirtualMethods(DexClass clazz) {
     DexMethodSignatureSet libraryMethodsOnClass = DexMethodSignatureSet.create();
     immediateSubtypingInfo.forEachImmediateSuperClassMatching(
         clazz,
         (supertype, superclass) -> superclass != null,
         (supertype, superclass) ->
-            libraryMethodsOnClass.addAll(getOrComputeLibraryMethods(superclass)));
+            libraryMethodsOnClass.addAll(getOrComputeLibraryVirtualMethods(superclass)));
     clazz.forEachClassMethodMatching(
         DexEncodedMethod::belongsToVirtualPool,
         method -> libraryMethodsOnClass.add(method.getMethodSignature()));
-    libraryMethods.put(clazz, libraryMethodsOnClass);
+    libraryVirtualMethods.put(clazz, libraryMethodsOnClass);
     return libraryMethodsOnClass;
   }
 
@@ -182,7 +183,8 @@
       DexMethodSignatureSet pinnedMethodSignatures = DexMethodSignatureSet.create();
       Set<DexClass> seenLibraryClasses = Sets.newIdentityHashSet();
       for (DexProgramClass clazz : stronglyConnectedProgramClasses) {
-        clazz.forEachProgramMethod(
+        clazz.forEachProgramMethodMatching(
+            method -> !method.isInstanceInitializer(),
             method -> {
               if (!appView.getKeepInfo(method).isShrinkingAllowed(options)) {
                 pinnedMethodSignatures.add(method.getMethodSignature());
@@ -195,7 +197,7 @@
                     && !superclass.isProgramClass()
                     && seenLibraryClasses.add(superclass),
             (supertype, superclass) ->
-                pinnedMethodSignatures.addAll(getOrComputeLibraryMethods(superclass)));
+                pinnedMethodSignatures.addAll(getOrComputeLibraryVirtualMethods(superclass)));
       }
       pinnedMethodSignatures.forEach(
           signature -> reserveMethodSignature(signature, signature, IntSets.EMPTY_SET));
@@ -306,11 +308,14 @@
     private boolean visitClass(
         DexProgramClass clazz, ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) {
       BooleanBox affected = new BooleanBox();
+      DexMethodSignatureSet instanceInitializerSignatures = DexMethodSignatureSet.create();
+      clazz.forEachProgramInstanceInitializer(instanceInitializerSignatures::add);
       clazz.forEachProgramMethod(
           method -> {
             ArgumentInfoCollection removableParameters =
                 method.getDefinition().belongsToDirectPool()
-                    ? computeRemovableParametersFromDirectMethod(method)
+                    ? computeRemovableParametersFromDirectMethod(
+                        method, instanceInitializerSignatures)
                     : computeRemovableParametersFromVirtualMethod(method);
             DexMethod newMethodSignature = getNewMethodSignature(method, removableParameters);
             if (newMethodSignature != method.getReference()) {
@@ -343,10 +348,12 @@
 
       // Find a method signature. First check if the current signature is available.
       if (!occupiedMethodSignatures.containsKey(methodSignatureWithParametersRemoved)) {
-        reserveMethodSignature(
-            methodSignatureWithParametersRemoved,
-            methodSignatureWithoutParametersRemoved,
-            removableParameterIndices);
+        if (!method.getDefinition().isInstanceInitializer()) {
+          reserveMethodSignature(
+              methodSignatureWithParametersRemoved,
+              methodSignatureWithoutParametersRemoved,
+              removableParameterIndices);
+        }
         return methodReferenceWithParametersRemoved;
       }
 
@@ -377,20 +384,20 @@
               });
 
       // Reserve the newly generated method signature.
-      reserveMethodSignature(
-          newMethod.getSignature(),
-          methodSignatureWithoutParametersRemoved,
-          removableParameterIndices);
+      if (!method.getDefinition().isInstanceInitializer()) {
+        reserveMethodSignature(
+            newMethod.getSignature(),
+            methodSignatureWithoutParametersRemoved,
+            removableParameterIndices);
+      }
 
       return newMethod;
     }
 
     private ArgumentInfoCollection computeRemovableParametersFromDirectMethod(
-        ProgramMethod method) {
+        ProgramMethod method, DexMethodSignatureSet instanceInitializerSignatures) {
       assert method.getDefinition().belongsToDirectPool();
-      // TODO(b/190154391): Allow parameter removal from initializers. We need to guarantee absence
-      //  of collisions since initializers can't be renamed.
-      if (!isParameterRemovalAllowed(method) || method.getDefinition().isInstanceInitializer()) {
+      if (!isParameterRemovalAllowed(method)) {
         return ArgumentInfoCollection.empty();
       }
       // TODO(b/199864962): Allow parameter removal from check-not-null classified methods.
@@ -400,7 +407,18 @@
           .isCheckNotNullClassification()) {
         return ArgumentInfoCollection.empty();
       }
-      return computeRemovableParametersFromMethod(method);
+      ArgumentInfoCollection removableParameters = computeRemovableParametersFromMethod(method);
+      if (removableParameters.isEmpty()) {
+        return removableParameters;
+      }
+      if (method.getDefinition().isInstanceInitializer()) {
+        DexMethod rewrittenMethod = removableParameters.rewriteMethod(method, dexItemFactory);
+        assert rewrittenMethod != method.getReference();
+        if (!instanceInitializerSignatures.add(rewrittenMethod)) {
+          return ArgumentInfoCollection.empty();
+        }
+      }
+      return removableParameters;
     }
 
     private ArgumentInfoCollection computeRemovableParametersFromVirtualMethod(
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
index b6f7c52..8ef1277 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
@@ -94,6 +94,10 @@
     return backing.contains(signature);
   }
 
+  public boolean contains(DexMethod method) {
+    return contains(method.getSignature());
+  }
+
   public boolean contains(DexEncodedMethod method) {
     return contains(method.getSignature());
   }
diff --git a/src/test/examples/classmerging/CallGraphCycleTest.java b/src/test/examples/classmerging/CallGraphCycleTest.java
index 40347d6..ea958d2 100644
--- a/src/test/examples/classmerging/CallGraphCycleTest.java
+++ b/src/test/examples/classmerging/CallGraphCycleTest.java
@@ -7,14 +7,14 @@
 public class CallGraphCycleTest {
 
   public static void main(String[] args) {
-    new B(true);
+    new B(args.length == 0, args.length == 1);
   }
 
   public static class A {
 
-    public A(boolean instantiateB) {
+    public A(boolean instantiateB, boolean alwaysFalse) {
       if (instantiateB) {
-        new B(false);
+        new B(alwaysFalse, alwaysFalse);
       }
       System.out.println("A(" + instantiateB + ")");
     }
@@ -22,8 +22,8 @@
 
   public static class B extends A {
 
-    public B(boolean instantiateBinA) {
-      super(instantiateBinA);
+    public B(boolean instantiateBinA, boolean alwaysFalse) {
+      super(instantiateBinA, alwaysFalse);
       System.out.println("B(" + instantiateBinA + ")");
     }
   }
diff --git a/src/test/examples/shaking1/Shaking.java b/src/test/examples/shaking1/Shaking.java
index 8c39ab8..924ae93 100644
--- a/src/test/examples/shaking1/Shaking.java
+++ b/src/test/examples/shaking1/Shaking.java
@@ -5,6 +5,7 @@
 
 public class Shaking {
   public static void main(String[] args) {
-    System.out.println(new Used("world").method());
+    String world = args.length == 0 ? "world" : null;
+    System.out.println(new Used(world).method());
   }
 }
diff --git a/src/test/examples/shaking1/print-mapping-cf.ref b/src/test/examples/shaking1/print-mapping-cf.ref
index a7e0b8e..8b38084 100644
--- a/src/test/examples/shaking1/print-mapping-cf.ref
+++ b/src/test/examples/shaking1/print-mapping-cf.ref
@@ -1,6 +1,7 @@
 shaking1.Shaking -> shaking1.Shaking:
 shaking1.Used -> a.a:
+    1:2:void <init>(java.lang.String):12:13 -> <init>
+    1:1:java.lang.String aMethodThatIsNotUsedButKept():21:21 -> aMethodThatIsNotUsedButKept
+    1:2:void main(java.lang.String[]):8:9 -> main
     1:1:java.lang.String method():17:17 -> a
-    1:1:void main(java.lang.String[]):8:8 -> main
-    1:1:void <init>(java.lang.String):12:12 -> <init>
-    1:1:java.lang.String aMethodThatIsNotUsedButKept():21:21 -> aMethodThatIsNotUsedButKept
\ No newline at end of file
+    java.lang.String name -> a
\ No newline at end of file
diff --git a/src/test/examples/shaking1/print-mapping-dex.ref b/src/test/examples/shaking1/print-mapping-dex.ref
index 7ba8ee8..5a9504a 100644
--- a/src/test/examples/shaking1/print-mapping-dex.ref
+++ b/src/test/examples/shaking1/print-mapping-dex.ref
@@ -1,5 +1,8 @@
 shaking1.Shaking -> shaking1.Shaking:
 shaking1.Used -> a.a:
-    java.lang.String method() -> a
-    0:16:void main(java.lang.String[]):8:8 -> main
-    0:3:void <init>(java.lang.String):12:12 -> <init>
+    0:2:void <init>(java.lang.String):12:12 -> <init>
+    3:5:void <init>(java.lang.String):13:13 -> <init>
+    0:6:void main(java.lang.String[]):8:8 -> main
+    7:21:void main(java.lang.String[]):9:9 -> main
+    0:19:java.lang.String method():17:17 -> a
+    java.lang.String name -> a
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index e0c6c9e..06bfb5b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -38,8 +38,7 @@
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(
-            "changed", "13", "42", "foo", "7", "foo", "print a", "print b")
+        .assertSuccessWithOutputLines("changed", "0", "42", "foo", "7", "foo", "print a", "print b")
         .inspect(
             codeInspector -> {
               ClassSubject changedClassSubject = codeInspector.clazz(Changed.class);
@@ -118,7 +117,7 @@
   public static class Main {
     public static void main(String[] args) {
       Parent p = new Changed();
-      A a = new A(13);
+      A a = new A(args.length);
       a = new A(p);
       B b = new B(p);
       a.print();
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
index 3917d56..6094157 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
@@ -75,14 +75,14 @@
                   isPresent());
             })
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("C", "42", "C", "D");
+        .assertSuccessWithOutputLines("C", "0", "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 A(new C(), args.length));
       System.out.println(new B(new D()));
     }
   }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
index dd9ab2a..9b45774 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
@@ -30,7 +30,7 @@
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("foo", "B", "bar", "5", "foobar")
+        .assertSuccessWithOutputLines("foo", "B", "bar", "0", "foobar")
         .inspect(
             codeInspector -> {
               assertThat(codeInspector.clazz(A.class), isAbsent());
@@ -56,7 +56,7 @@
       a.foo();
       B b = a.get("B");
       b.bar();
-      C c = new C(5);
+      C c = new C(args.length);
       c.foobar();
     }
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
index 48d3203..3c76052 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
@@ -58,11 +58,9 @@
               counts.println = countInvokesWithName(methodSubject, "println");
             });
 
-    // TODO(b/129044633) We expect 0 x run and 2 x println after we can inline those methods.
-    assertEquals(2, counts.run);
-    assertEquals(0, counts.println);
-    // TODO(b/126323172) After this issue has been fixed, add test here to check the same with
-    // desugared lambdas.
+    // TODO(b/126323172) Add test here to check the same with desugared lambdas.
+    assertEquals(0, counts.run);
+    assertEquals(2, counts.println);
   }
 
   private static long countInvokesWithName(MethodSubject methodSubject, String name) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
index c008972..c074ee1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
@@ -82,7 +82,9 @@
         .addKeepRules(
             "-keep @interface Annotation?",
             "-neverclassinline class *",
-            "-nohorizontalclassmerging class Test$Inner?")
+            "-nohorizontalclassmerging class Test$Inner?",
+            "-keepclassmembers class Test$Inner? { synthetic <fields>; }",
+            "-keepconstantarguments class Test$Inner? { void <init>(...); }")
         .addKeepRuntimeVisibleParameterAnnotations()
         .enableProguardTestOptions()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index aebbbb2..2cb21d4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -6,10 +6,8 @@
 
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -78,9 +76,7 @@
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
-              if (allowAccessModification
-                  && kotlinParameters.is(
-                      KotlinCompilerVersion.KOTLINC_1_5_0, KotlinTargetVersion.JAVA_8)) {
+              if (allowAccessModification) {
                 checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
               } else {
                 ClassSubject dataClass =
@@ -129,9 +125,7 @@
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
-              if (allowAccessModification
-                  && kotlinParameters.is(
-                      KotlinCompilerVersion.KOTLINC_1_5_0, KotlinTargetVersion.JAVA_8)) {
+              if (allowAccessModification) {
                 checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
               } else {
                 ClassSubject dataClass =
@@ -181,21 +175,26 @@
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
-              ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
+              } else {
+                ClassSubject dataClass =
+                    checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
 
-              boolean component2IsPresent = !allowAccessModification;
-              checkMethodIsKeptOrRemoved(dataClass, COMPONENT2_METHOD, component2IsPresent);
+                boolean component2IsPresent = !allowAccessModification;
+                checkMethodIsKeptOrRemoved(dataClass, COMPONENT2_METHOD, component2IsPresent);
 
-              // Function component1 is not used.
-              checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
+                // Function component1 is not used.
+                checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
 
-              // No use of getter.
-              checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
-              checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
+                // No use of getter.
+                checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
+                checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
 
-              // No use of copy functions.
-              checkMethodIsRemoved(dataClass, COPY_METHOD);
-              checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+                // No use of copy functions.
+                checkMethodIsRemoved(dataClass, COPY_METHOD);
+                checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+              }
 
               ClassSubject classSubject = checkClassIsKept(inspector, mainClassName);
               MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
@@ -222,9 +221,7 @@
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
-              if (testParameters.isDexRuntime()
-                  && allowAccessModification
-                  && kotlinParameters.is(KotlinCompilerVersion.KOTLINC_1_3_72)) {
+              if (allowAccessModification) {
                 checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
               } else {
                 ClassSubject dataClass =