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/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index ebceefd..119dcb5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -476,7 +476,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;
       }
@@ -485,6 +486,32 @@
         // 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()
+                  .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 {