Revert "Reland "Fix enum merging with abstract method error""

This reverts commit 67951b6fb8db259454c110f1294e75a57cdc11ee.

Reason for revert: Breaks bots

Change-Id: I8c8cdfb32d0566b0738ef5fb406756a37d6563b3
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 2db3697..32f0cbd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -327,8 +327,6 @@
       createString("Ljava/lang/IllegalAccessError;");
   public final DexString illegalArgumentExceptionDescriptor =
       createString("Ljava/lang/IllegalArgumentException;");
-  public final DexString abstractMethodErrorDescriptor =
-      createString("Ljava/lang/AbstractMethodError;");
   public final DexString icceDescriptor = createString("Ljava/lang/IncompatibleClassChangeError;");
   public final DexString exceptionInInitializerErrorDescriptor =
       createString("Ljava/lang/ExceptionInInitializerError;");
@@ -575,8 +573,6 @@
       createStaticallyKnownType(illegalAccessErrorDescriptor);
   public final DexType illegalArgumentExceptionType =
       createStaticallyKnownType(illegalArgumentExceptionDescriptor);
-  public final DexType abstractMethodErrorType =
-      createStaticallyKnownType(abstractMethodErrorDescriptor);
   public final DexType icceType = createStaticallyKnownType(icceDescriptor);
   public final DexType exceptionInInitializerErrorType =
       createStaticallyKnownType(exceptionInInitializerErrorDescriptor);
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 7449cee..6ba6ad1 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
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl.ordinalToUnboxedInt;
 
-import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -61,9 +60,8 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
-import com.android.tools.r8.ir.synthetic.EnumUnboxingCfCodeProvider.CfCodeWithLens;
 import com.android.tools.r8.ir.synthetic.EnumUnboxingCfCodeProvider.EnumUnboxingMethodDispatchCfCodeProvider;
-import com.android.tools.r8.ir.synthetic.ThrowCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.EnumUnboxingCfCodeProvider.EnumUnboxingMethodDispatchCfCodeProvider.CfCodeWithLens;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ImmutableArrayUtils;
@@ -656,13 +654,10 @@
     // and at least one override.
     DexMethod reference = nonPrivateVirtualMethod.withHolder(unboxedEnum.getType(), factory);
     ProgramMethodSet subimplementations = ProgramMethodSet.create();
-    boolean allImplements = true;
     for (DexProgramClass subEnum : subEnums) {
       ProgramMethod subMethod = subEnum.lookupProgramMethod(reference);
       if (subMethod != null) {
         subimplementations.add(subMethod);
-      } else {
-        allImplements = false;
       }
     }
     DexClassAndMethod superMethod = unboxedEnum.lookupProgramMethod(reference);
@@ -671,73 +666,60 @@
       superMethod = appView.appInfo().lookupSuperTarget(reference, unboxedEnum, appView);
       assert superMethod == null || superMethod.getReference() == factory.enumMembers.toString;
     }
-    if (superMethod == null) {
-      // No effective virtual dispatch is required, just move each subimplementation.
+    if (superMethod == null || subimplementations.isEmpty()) {
+      // No emulated dispatch is required, just move everything.
+      // If an abstract method with no implementors is found, effectively don't do anything.
+      if (superMethod != null && !superMethod.getAccessFlags().isAbstract()) {
+        assert superMethod.isProgramMethod();
+        directMoveAndMap(localUtilityClass, localUtilityMethods, superMethod.asProgramMethod());
+      }
       for (ProgramMethod override : subimplementations) {
-        assert !override.getAccessFlags().isAbstract();
         directMoveAndMap(localUtilityClass, localUtilityMethods, override);
       }
       return;
     }
-    if (superMethod.getAccessFlags().isAbstract()) {
-      if (subimplementations.isEmpty()) {
-        // Abstract method with no implementors: rewrite to abstract method error.
-        directMoveAndMap(
-            localUtilityClass, localUtilityMethods, superMethod.asProgramMethod(), true);
-      } else if (!allImplements) {
-        // The abstract method is missing implementors, so we need to remap all missing
-        // implementation to an abstract method error.
-        emulatedDispatchMoveAndMap(
-            localUtilityClass, localUtilityMethods, superMethod, subimplementations, true);
-      } else if (subimplementations.size() == 1) {
-        // Single implementor, no emulated dispatch is required, just forward everything to the
-        // unique implementation.
-        assert allImplements;
-        ProgramMethod override = subimplementations.iterator().next();
-        DexMethod uniqueUtility =
-            directMoveAndMap(localUtilityClass, localUtilityMethods, override);
-        lensBuilder.mapToDispatch(superMethod.getReference(), uniqueUtility);
-      } else {
-        // Multiple implementors, the abstract method is entirely implemented, no need to
-        // introduce the call to the abstract method error.
-        emulatedDispatchMoveAndMap(
-            localUtilityClass, localUtilityMethods, superMethod, subimplementations, false);
-      }
+    if (superMethod.getDefinition().isAbstract() && subimplementations.size() == 1) {
+      // No emulated dispatch is required, just forward everything to the unique implementation.
+      ProgramMethod override = subimplementations.iterator().next();
+      DexMethod uniqueUtility = directMoveAndMap(localUtilityClass, localUtilityMethods, override);
+      lensBuilder.mapToDispatch(superMethod.getReference(), uniqueUtility);
       return;
     }
-    assert !superMethod.getAccessFlags().isAbstract();
-    // No override, no effective virtual dispatch, just forward to the unique implementation.
-    if (subimplementations.isEmpty()) {
-      assert superMethod.isProgramMethod();
-      directMoveAndMap(localUtilityClass, localUtilityMethods, superMethod.asProgramMethod());
-      return;
-    }
-    // Emulated dispatch with a default case on the super enum.
+    // These methods require emulated dispatch.
     emulatedDispatchMoveAndMap(
-        localUtilityClass, localUtilityMethods, superMethod, subimplementations, false);
+        localUtilityClass, localUtilityMethods, superMethod, subimplementations);
   }
 
   private void emulatedDispatchMoveAndMap(
       LocalEnumUnboxingUtilityClass localUtilityClass,
       Map<DexMethod, DexEncodedMethod> localUtilityMethods,
       DexClassAndMethod superMethod,
-      ProgramMethodSet unorderedSubimplementations,
-      boolean needsAbstractMethodErrorCase) {
+      ProgramMethodSet unorderedSubimplementations) {
     assert !unorderedSubimplementations.isEmpty();
     DexMethod superUtilityMethod;
     List<ProgramMethod> sortedSubimplementations = new ArrayList<>(unorderedSubimplementations);
     sortedSubimplementations.sort(Comparator.comparing(ProgramMethod::getHolderType));
-    superUtilityMethod =
-        computeSuperUtilityMethod(
-            localUtilityClass,
-            localUtilityMethods,
-            superMethod,
-            sortedSubimplementations,
-            needsAbstractMethodErrorCase);
+    if (superMethod.isProgramMethod()) {
+      superUtilityMethod =
+          installLocalUtilityMethod(
+              localUtilityClass, localUtilityMethods, superMethod.asProgramMethod());
+    } else {
+      // All methods but toString() are final or non-virtual.
+      // We could support other cases by setting correctly the superUtilityMethod here.
+      assert superMethod.getReference().match(factory.enumMembers.toString);
+      ProgramMethod toString = localUtilityClass.ensureToStringMethod(appView);
+      superUtilityMethod = toString.getReference();
+      for (ProgramMethod context : sortedSubimplementations) {
+        // If the utility method is used only from the dispatch method, we have to process it and
+        // add it to the ArtProfile.
+        methodsToProcess.add(toString);
+        profileCollectionAdditions.addMethodIfContextIsInProfile(toString, context);
+      }
+    }
     Map<DexMethod, DexMethod> overrideToUtilityMethods = new IdentityHashMap<>();
     for (ProgramMethod subMethod : sortedSubimplementations) {
       DexMethod subEnumLocalUtilityMethod =
-          installLocalUtilityMethod(localUtilityClass, localUtilityMethods, subMethod, false);
+          installLocalUtilityMethod(localUtilityClass, localUtilityMethods, subMethod);
       assert subEnumLocalUtilityMethod != null;
       overrideToUtilityMethods.put(subMethod.getReference(), subEnumLocalUtilityMethod);
     }
@@ -766,58 +748,13 @@
     }
   }
 
-  private DexMethod computeSuperUtilityMethod(
-      LocalEnumUnboxingUtilityClass localUtilityClass,
-      Map<DexMethod, DexEncodedMethod> localUtilityMethods,
-      DexClassAndMethod superMethod,
-      List<ProgramMethod> sortedSubimplementations,
-      boolean needsAbstractMethodErrorCase) {
-    DexMethod superUtilityMethod;
-    if (superMethod.isProgramMethod()) {
-      if (needsAbstractMethodErrorCase) {
-        assert superMethod.getAccessFlags().isAbstract();
-        superUtilityMethod =
-            installLocalUtilityMethod(
-                localUtilityClass, localUtilityMethods, superMethod.asProgramMethod(), true);
-      } else if (!superMethod.getAccessFlags().isAbstract()) {
-        superUtilityMethod =
-            installLocalUtilityMethod(
-                localUtilityClass, localUtilityMethods, superMethod.asProgramMethod(), false);
-      } else {
-        assert superMethod.getAccessFlags().isAbstract();
-        superUtilityMethod = null;
-      }
-    } else {
-      // All methods but toString() are final or non-virtual.
-      // We could support other cases by setting correctly the superUtilityMethod here.
-      assert superMethod.getReference().match(factory.enumMembers.toString);
-      ProgramMethod toString = localUtilityClass.ensureToStringMethod(appView);
-      superUtilityMethod = toString.getReference();
-      for (ProgramMethod context : sortedSubimplementations) {
-        // If the utility method is used only from the dispatch method, we have to process it and
-        // add it to the ArtProfile.
-        methodsToProcess.add(toString);
-        profileCollectionAdditions.addMethodIfContextIsInProfile(toString, context);
-      }
-    }
-    return superUtilityMethod;
-  }
-
   private DexMethod directMoveAndMap(
       LocalEnumUnboxingUtilityClass localUtilityClass,
       Map<DexMethod, DexEncodedMethod> localUtilityMethods,
       ProgramMethod method) {
-    return directMoveAndMap(localUtilityClass, localUtilityMethods, method, false);
-  }
-
-  private DexMethod directMoveAndMap(
-      LocalEnumUnboxingUtilityClass localUtilityClass,
-      Map<DexMethod, DexEncodedMethod> localUtilityMethods,
-      ProgramMethod method,
-      boolean abstractMethodError) {
+    assert !method.getAccessFlags().isAbstract();
     DexMethod utilityMethod =
-        installLocalUtilityMethod(
-            localUtilityClass, localUtilityMethods, method, abstractMethodError);
+        installLocalUtilityMethod(localUtilityClass, localUtilityMethods, method);
     assert utilityMethod != null;
     lensBuilder.moveAndMap(method.getReference(), utilityMethod, method.getDefinition().isStatic());
     return utilityMethod;
@@ -892,15 +829,15 @@
   private DexMethod installLocalUtilityMethod(
       LocalEnumUnboxingUtilityClass localUtilityClass,
       Map<DexMethod, DexEncodedMethod> localUtilityMethods,
-      ProgramMethod method,
-      boolean abstractMethodError) {
-    assert abstractMethodError || !method.getAccessFlags().isAbstract();
-    Predicate<DexMethod> isFresh =
-        newMethodSignature -> !localUtilityMethods.containsKey(newMethodSignature);
+      ProgramMethod method) {
+    if (method.getAccessFlags().isAbstract()) {
+      return null;
+    }
     DexEncodedMethod newLocalUtilityMethod =
-        abstractMethodError
-            ? createAbstractMethodErrorLocalUtilityMethod(method, localUtilityClass, isFresh)
-            : createLocalUtilityMethod(method, localUtilityClass, isFresh);
+        createLocalUtilityMethod(
+            method,
+            localUtilityClass,
+            newMethodSignature -> !localUtilityMethods.containsKey(newMethodSignature));
     assert !localUtilityMethods.containsKey(newLocalUtilityMethod.getReference());
     localUtilityMethods.put(newLocalUtilityMethod.getReference(), newLocalUtilityMethod);
     return newLocalUtilityMethod.getReference();
@@ -910,78 +847,40 @@
       ProgramMethod method,
       LocalEnumUnboxingUtilityClass localUtilityClass,
       Predicate<DexMethod> availableMethodSignatures) {
-    assert !method.getAccessFlags().isAbstract();
+    DexMethod methodReference = method.getReference();
+
+    // Create a new, fresh method signature on the local utility class. We prefix the method by "_"
+    // such that this does not collide with the utility methods we synthesize for unboxing.
     DexMethod newMethod =
-        createFreshMethodSignature(
-            method, localUtilityClass, availableMethodSignatures, method.getReference());
+        method.getDefinition().isClassInitializer()
+            ? factory.createClassInitializer(localUtilityClass.getType())
+            : factory.createFreshMethodNameWithoutHolder(
+                "_" + method.getName().toString(),
+                fixupProto(
+                    method.getAccessFlags().isStatic()
+                        ? method.getProto()
+                        : factory.prependHolderToProto(methodReference)),
+                localUtilityClass.getType(),
+                availableMethodSignatures);
+
     return method
         .getDefinition()
         .toTypeSubstitutedMethod(
             newMethod,
             builder ->
-                transformMethodForLocalUtility(builder, method)
-                    .setCompilationState(method.getDefinition().getCompilationState()));
-  }
-
-  private DexEncodedMethod createAbstractMethodErrorLocalUtilityMethod(
-      ProgramMethod method,
-      LocalEnumUnboxingUtilityClass localUtilityClass,
-      Predicate<DexMethod> availableMethodSignatures) {
-    assert method.getAccessFlags().isAbstract();
-    DexMethod newMethod =
-        createFreshMethodSignature(
-            method, localUtilityClass, availableMethodSignatures, method.getReference());
-    DexEncodedMethod dexEncodedMethod =
-        method
-            .getDefinition()
-            .toTypeSubstitutedMethod(
-                newMethod,
-                builder ->
-                    transformMethodForLocalUtility(builder, method)
-                        .modifyAccessFlags(MethodAccessFlags::unsetAbstract)
-                        .setCode(
-                            new ThrowCfCodeProvider(
-                                    appView, newMethod, factory.abstractMethodErrorType)
-                                .generateCfCode())
-                        .setClassFileVersion(CfVersion.V1_8)
-                        .setApiLevelForDefinition(appView.computedMinApiLevel())
-                        .setApiLevelForCode(appView.computedMinApiLevel()));
-    methodsToProcess.add(new ProgramMethod(localUtilityClass.getDefinition(), dexEncodedMethod));
-    return dexEncodedMethod;
-  }
-
-  private DexEncodedMethod.Builder transformMethodForLocalUtility(
-      DexEncodedMethod.Builder builder, ProgramMethod method) {
-    builder
-        .clearAllAnnotations()
-        .modifyAccessFlags(
-            accessFlags -> {
-              if (method.getDefinition().isClassInitializer()) {
-                assert accessFlags.isStatic();
-              } else {
-                accessFlags.promoteToPublic();
-                accessFlags.promoteToStatic();
-              }
-            })
-        .unsetIsLibraryMethodOverride();
-    return builder;
-  }
-
-  private DexMethod createFreshMethodSignature(
-      ProgramMethod method,
-      LocalEnumUnboxingUtilityClass localUtilityClass,
-      Predicate<DexMethod> availableMethodSignatures,
-      DexMethod methodReference) {
-    return method.getDefinition().isClassInitializer()
-        ? factory.createClassInitializer(localUtilityClass.getType())
-        : factory.createFreshMethodNameWithoutHolder(
-            "_" + method.getName().toString(),
-            fixupProto(
-                method.getAccessFlags().isStatic()
-                    ? method.getProto()
-                    : factory.prependHolderToProto(methodReference)),
-            localUtilityClass.getType(),
-            availableMethodSignatures);
+                builder
+                    .clearAllAnnotations()
+                    .modifyAccessFlags(
+                        accessFlags -> {
+                          if (method.getDefinition().isClassInitializer()) {
+                            assert accessFlags.isStatic();
+                          } else {
+                            accessFlags.promoteToPublic();
+                            accessFlags.promoteToStatic();
+                          }
+                        })
+                    .setCompilationState(method.getDefinition().getCompilationState())
+                    .unsetIsLibraryMethodOverride());
   }
 
   private boolean isPrunedAfterEnumUnboxing(ProgramField field, EnumData enumData) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
index f60fe64..112ffdd 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -169,6 +169,25 @@
               ? new CfReturnVoid()
               : new CfReturn(ValueType.fromDexType(method.getReturnType())));
     }
+
+    public static class CfCodeWithLens extends CfCode {
+      private GraphLens codeLens;
+
+      public void setCodeLens(GraphLens codeLens) {
+        this.codeLens = codeLens;
+      }
+
+      public CfCodeWithLens(
+          DexType originalHolder, int maxStack, int maxLocals, List<CfInstruction> instructions) {
+        super(originalHolder, maxStack, maxLocals, instructions);
+      }
+
+      @Override
+      public GraphLens getCodeLens(AppView<?> appView) {
+        assert codeLens != null;
+        return codeLens;
+      }
+    }
   }
 
   public static class EnumUnboxingInstanceFieldCfCodeProvider extends EnumUnboxingCfCodeProvider {
@@ -303,24 +322,4 @@
       return standardCfCodeFromInstructions(instructions);
     }
   }
-
-  public static class CfCodeWithLens extends CfCode {
-
-    private GraphLens codeLens;
-
-    public void setCodeLens(GraphLens codeLens) {
-      this.codeLens = codeLens;
-    }
-
-    public CfCodeWithLens(
-        DexType originalHolder, int maxStack, int maxLocals, List<CfInstruction> instructions) {
-      super(originalHolder, maxStack, maxLocals, instructions);
-    }
-
-    @Override
-    public GraphLens getCodeLens(AppView<?> appView) {
-      assert codeLens != null;
-      return codeLens;
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ThrowCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/ThrowCfCodeProvider.java
deleted file mode 100644
index becf6d3..0000000
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ThrowCfCodeProvider.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2023, 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.synthetic;
-
-import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
-
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfNew;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
-import com.android.tools.r8.cf.code.CfThrow;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Generates a method that just throws a exception with empty <init></init> with *any* signature
- * passed, so the method can be inserted in a hierarchy and be called with normal virtual dispatch.
- */
-public class ThrowCfCodeProvider extends SyntheticCfCodeProvider {
-
-  private final DexMethod method;
-  private final DexType exceptionType;
-
-  public ThrowCfCodeProvider(AppView<?> appView, DexMethod method, DexType exceptionType) {
-    super(appView, method.getHolderType());
-    this.method = method;
-    this.exceptionType = exceptionType;
-  }
-
-  @Override
-  public CfCode generateCfCode() {
-    List<CfInstruction> instructions = new ArrayList<>();
-    instructions.add(new CfNew(exceptionType));
-    instructions.add(new CfStackInstruction(Opcode.Dup));
-    DexMethod init = appView.dexItemFactory().createInstanceInitializer(exceptionType);
-    instructions.add(new CfInvoke(INVOKESPECIAL, init, false));
-    instructions.add(new CfThrow());
-    return standardCfCodeFromInstructions(instructions);
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/enummerging/AbstractMethodErrorEnumMergingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/AbstractMethodErrorEnumMergingTest.java
deleted file mode 100644
index 7937ef7..0000000
--- a/src/test/java/com/android/tools/r8/enumunboxing/enummerging/AbstractMethodErrorEnumMergingTest.java
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright (c) 2023, 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.enumunboxing.enummerging;
-
-import static com.android.tools.r8.ToolHelper.getClassFilesForInnerClasses;
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase;
-import com.android.tools.r8.utils.StringUtils;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-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 AbstractMethodErrorEnumMergingTest extends EnumUnboxingTestBase {
-
-  private final TestParameters parameters;
-  private final boolean enumValueOptimization;
-  private final EnumKeepRules enumKeepRules;
-  private final String EXPECTED_RESULT =
-      StringUtils.lines(
-          "class java.lang.AbstractMethodError",
-          "74",
-          "class java.lang.AbstractMethodError",
-          "44",
-          "class java.lang.AbstractMethodError",
-          "class java.lang.AbstractMethodError");
-
-  @Parameters(name = "{0} valueOpt: {1} keep: {2}")
-  public static List<Object[]> data() {
-    return enumUnboxingTestParameters();
-  }
-
-  public AbstractMethodErrorEnumMergingTest(
-      TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
-    this.parameters = parameters;
-    this.enumValueOptimization = enumValueOptimization;
-    this.enumKeepRules = enumKeepRules;
-  }
-
-  @Test
-  public void testReference() throws Exception {
-    testForD8(parameters.getBackend())
-        .addProgramClassFileData(inputProgram())
-        .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
-        .setMinApi(parameters)
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutput(EXPECTED_RESULT);
-  }
-
-  @Test
-  public void testEnumUnboxing() throws Exception {
-    testForR8(parameters.getBackend())
-        .addProgramClassFileData(inputProgram())
-        .addKeepMainRule(Main.class)
-        .addKeepRules(enumKeepRules.getKeepRules())
-        .addEnumUnboxingInspector(
-            inspector -> inspector.assertUnboxed(MyEnum2Cases.class, MyEnum1Case.class))
-        .enableInliningAnnotations()
-        .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
-        .setMinApi(parameters)
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutput(EXPECTED_RESULT);
-  }
-
-  private List<byte[]> inputProgram() throws Exception {
-    Collection<Path> files = getClassFilesForInnerClasses(getClass());
-    List<byte[]> result = new ArrayList<>();
-    int changed = 0;
-    for (Path file : files) {
-      String fileName = file.getFileName().toString();
-      if (fileName.equals("AbstractMethodErrorEnumMergingTest$MyEnum1Case$1.class")
-          || fileName.equals("AbstractMethodErrorEnumMergingTest$MyEnum2Cases$1.class")) {
-        result.add(transformer(file, null).removeMethodsWithName("operate").transform());
-        changed++;
-      } else {
-        result.add(Files.readAllBytes(file));
-      }
-    }
-    assertEquals(2, changed);
-    return result;
-  }
-
-  enum MyEnum2Cases {
-    A(8) {
-      // Will be removed by transformation before compilation.
-      @NeverInline
-      @Override
-      public long operate(long another) {
-        throw new RuntimeException("Should have been removed");
-      }
-    },
-    B(32) {
-      @NeverInline
-      @Override
-      public long operate(long another) {
-        return num + another;
-      }
-    };
-    final long num;
-
-    MyEnum2Cases(long num) {
-      this.num = num;
-    }
-
-    public abstract long operate(long another);
-  }
-
-  enum MyEnum1Case {
-    A(8) {
-      // Will be removed by transformation before compilation.
-      @NeverInline
-      @Override
-      public long operate(long another) {
-        throw new RuntimeException("Should have been removed");
-      }
-    };
-    final long num;
-
-    MyEnum1Case(long num) {
-      this.num = num;
-    }
-
-    public abstract long operate(long another);
-  }
-
-  static class Main {
-
-    public static void main(String[] args) {
-      try {
-        System.out.println(MyEnum2Cases.A.operate(42));
-      } catch (Throwable t) {
-        System.out.println(t.getClass());
-      }
-      System.out.println(MyEnum2Cases.B.operate(42));
-      try {
-        System.out.println(indirect(MyEnum2Cases.A));
-      } catch (Throwable t) {
-        System.out.println(t.getClass());
-      }
-      System.out.println(indirect(MyEnum2Cases.B));
-
-      try {
-        System.out.println(MyEnum1Case.A.operate(42));
-      } catch (Throwable t) {
-        System.out.println(t.getClass());
-      }
-      try {
-        System.out.println(indirect(MyEnum1Case.A));
-      } catch (Throwable t) {
-        System.out.println(t.getClass());
-      }
-    }
-
-    @NeverInline
-    public static long indirect(MyEnum2Cases e) {
-      return e.operate(12);
-    }
-
-    @NeverInline
-    public static long indirect(MyEnum1Case e) {
-      return e.operate(7);
-    }
-  }
-}