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();
+ }
+ }
+}