Ensure out-value of Enum.valueOf is analyzed by enum unboxer
Bug: b/241469650
Change-Id: Iacb3a41b00cc2afb4b2d4e5f26e763a9bc5202b4
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 522866a..23a09f7 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
@@ -489,7 +489,8 @@
}
if (user.isInvokeStatic()) {
- DexClassAndMethod singleTarget = user.asInvokeStatic().lookupSingleTarget(appView, context);
+ InvokeStatic invoke = user.asInvokeStatic();
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget == null) {
return false;
}
@@ -498,6 +499,33 @@
// in the valueOf utility method.
addRequiredNameData(enumClass);
markMethodDependsOnLibraryModelisation(context);
+ // The out-value must be cast before it is used, or an assume instruction must strengthen
+ // its dynamic type, so that the out-value is analyzed by the enum unboxing analysis.
+ if (invoke.hasOutValue()) {
+ if (invoke.outValue().hasPhiUsers()) {
+ return false;
+ }
+ for (Instruction enumUser : invoke.outValue().uniqueUsers()) {
+ if (enumUser.isAssumeWithDynamicTypeAssumption()) {
+ Assume assume = enumUser.asAssume();
+ if (assume
+ .getDynamicTypeAssumption()
+ .getDynamicType()
+ .getDynamicUpperBoundType()
+ .equalUpToNullability(enumClass.getType().toTypeElement(appView))) {
+ // OK.
+ continue;
+ }
+ } else if (enumUser.isCheckCast()) {
+ CheckCast checkCast = enumUser.asCheckCast();
+ if (checkCast.getType() == enumClass.getType()) {
+ // OK.
+ continue;
+ }
+ }
+ return false;
+ }
+ }
return true;
}
if (singleTarget.getReference()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfWithoutCastEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfWithoutCastEnumUnboxingTest.java
index d56df49..e213b3b 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfWithoutCastEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfWithoutCastEnumUnboxingTest.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.codeinspector.AssertUtils;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,20 +37,17 @@
@Test
public void testEnumUnboxing() throws Exception {
- AssertUtils.assertFailsCompilationIf(
- parameters.isCfRuntime(),
- () ->
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepClassAndMembersRules(Main.class)
- .addKeepRules(enumKeepRules.getKeepRules())
- .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
- .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .enableInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatThrows(VerifyError.class));
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertNotUnboxed(MyEnum.class))
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A");
}
static class Main {