Unbox enum with subtypes and super invoke

Bug: b/271385332
Change-Id: Ia99431a5114756d89a34a69821e3c1253aa1ba82
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index ef10ff8..27cda2d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -1393,7 +1393,8 @@
       // enum's type.
       for (int i = 0; i < singleTarget.getParameters().size(); i++) {
         if (invoke.getArgumentForParameter(i) == enumValue
-            && singleTarget.getParameter(i).toBaseType(factory) != enumClass.getType()) {
+            && !enumUnboxingCandidatesInfo.isAssignableTo(
+                singleTarget.getParameter(i).toBaseType(factory), enumClass.getType())) {
           return new IllegalInvokeWithImpreciseParameterTypeReason(singleTargetReference);
         }
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 56a2d20..63595f7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.enums;
 
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -103,10 +102,21 @@
     DexMethod result;
     if (previous.getType() == InvokeType.SUPER) {
       assert context != null;
-      DexType superEnum = unboxedEnums.representativeType(context.getHolderType());
-      if (superEnum != context.getHolderType()) {
-        // TODO(b/271385332): implement this.
-        throw new Unreachable();
+      DexMethod previousContext = getPreviousMethodSignature(context);
+      DexType superEnum = unboxedEnums.representativeType(previousContext.getHolderType());
+      if (unboxedEnums.isUnboxedEnum(superEnum)) {
+        if (superEnum != previousContext.getHolderType()) {
+          DexMethod reference = previous.getReference();
+          if (reference.getHolderType() != superEnum) {
+            // Rebind the reference to the superEnum if that is not the case.
+            reference = reference.withHolder(superEnum, dexItemFactory());
+          }
+          result = newMethodSignatures.getRepresentativeValue(reference);
+        } else {
+          // This is a super-invoke to a library method, not rewritten by the lens.
+          // This is rewritten by the EnumUnboxerRewriter.
+          return previous;
+        }
       } else {
         result = methodMap.apply(previous.getReference());
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 4e50543..94470b9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -78,7 +78,7 @@
   }
 
   private LocalEnumUnboxingUtilityClass getLocalUtilityClass(DexType enumType) {
-    return utilityClasses.getLocalUtilityClass(enumType);
+    return utilityClasses.getLocalUtilityClass(unboxedEnumsData.representativeType(enumType));
   }
 
   private SharedEnumUnboxingUtilityClass getSharedUtilityClass() {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/enummerging/SuperEnumMergingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/SuperEnumMergingTest.java
new file mode 100644
index 0000000..7a2b509
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/SuperEnumMergingTest.java
@@ -0,0 +1,124 @@
+// 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 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.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 SuperEnumMergingTest extends EnumUnboxingTestBase {
+
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines(
+          "a", "top", "A", "A", "A", "> A", "subA", "=", "B", "B", "=", "c", "top", "C", "C", "C",
+          "> C", "subB");
+
+  private final TestParameters parameters;
+  private final boolean enumValueOptimization;
+  private final EnumKeepRules enumKeepRules;
+
+  @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+  public static List<Object[]> data() {
+    return enumUnboxingTestParameters();
+  }
+
+  public SuperEnumMergingTest(
+      TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+    this.parameters = parameters;
+    this.enumValueOptimization = enumValueOptimization;
+    this.enumKeepRules = enumKeepRules;
+  }
+
+  @Test
+  public void testEnumUnboxing() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(SuperEnumMergingTest.class)
+        .addKeepMainRule(Main.class)
+        .addKeepRules(enumKeepRules.getKeepRules())
+        .addOptionsModification(opt -> opt.testing.enableEnumWithSubtypesUnboxing = true)
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumWithSuper.class))
+        .enableInliningAnnotations()
+        .enableMemberValuePropagationAnnotations()
+        .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+        .addOptionsModification(opt -> opt.testing.enableEnumUnboxingDebugLogs = true)
+        .setMinApi(parameters)
+        .allowDiagnosticInfoMessages()
+        .allowUnusedProguardConfigurationRules()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  enum EnumWithSuper {
+    A {
+      @NeverInline
+      @Override
+      public void method() {
+        System.out.println("a");
+        super.top();
+        super.method();
+        System.out.println(super.name());
+        System.out.println(super.toString());
+        sub();
+      }
+
+      @NeverInline
+      public void sub() {
+        System.out.println("subA");
+      }
+    },
+    B,
+    C {
+      @NeverInline
+      @Override
+      public void method() {
+        System.out.println("c");
+        super.top();
+        super.method();
+        System.out.println(super.name());
+        System.out.println(super.toString());
+        sub();
+      }
+
+      @NeverInline
+      public void sub() {
+        System.out.println("subB");
+      }
+    };
+
+    @NeverInline
+    public void method() {
+      System.out.println(super.name());
+      System.out.println(super.toString());
+    }
+
+    @NeverInline
+    @Override
+    public String toString() {
+      return "> " + name();
+    }
+
+    @NeverInline
+    public void top() {
+      System.out.println("top");
+    }
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      EnumWithSuper.A.method();
+      System.out.println("=");
+      EnumWithSuper.B.method();
+      System.out.println("=");
+      EnumWithSuper.C.method();
+    }
+  }
+}