Trace the values() method on emuns when used as default values in annotations

When an annotation have a field of an enum type with a default value then Java VM
will use the values() method on that enum class.

Bug: 137392797
Bug: 138156533
Change-Id: Ie6f26326f6315380ccac7f664d78ef0a174ee49c
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 67056fe..35cd3d1 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1692,7 +1692,7 @@
     DexEncodedMethod valuesMethod = clazz.lookupMethod(generatedEnumValuesMethod(clazz));
     if (valuesMethod != null) {
       // TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
-      // marking of not renaming it in the root set.
+      // marking for not renaming it is in the root set.
       enqueueRootItem(valuesMethod, reason);
       rootSet.shouldNotBeMinified(valuesMethod.toReference());
     }
@@ -2625,6 +2625,15 @@
             return false;
           }
           markStaticFieldAsLive(field, KeepReason.referencedInAnnotation(annotationHolder));
+          // When an annotation has a field of an enum type with a default value then Java VM
+          // will use the values() method on that enum class.
+          if (options.isGeneratingClassFiles()
+              && annotationHolder == dexItemFactory.annotationDefault) {
+            DexClass clazz = appView.definitionFor(field.type);
+            if (clazz != null && clazz.isProgramClass() && clazz.accessFlags.isEnum()) {
+              markEnumValuesAsReachable(clazz, KeepReason.referencedInAnnotation(annotationHolder));
+            }
+          }
         }
       } else {
         target = holder.lookupInstanceField(field);
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index 47fae5a..8585a9c 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -10,10 +10,10 @@
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.Collection;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -29,14 +29,17 @@
 public class B137392797 extends TestBase implements Opcodes {
 
   private final TestParameters parameters;
+  private final boolean defaultEnumValueInAnnotation;
 
-  @Parameterized.Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+  @Parameterized.Parameters(name = "Backend: {0}, default value in annotation: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevels().build(), BooleanUtils.values());
   }
 
-  public B137392797(TestParameters parameters) {
+  public B137392797(TestParameters parameters, boolean defaultEnumValueInAnnotation) {
     this.parameters = parameters;
+    this.defaultEnumValueInAnnotation = defaultEnumValueInAnnotation;
   }
 
   private void checkEnumUses(CodeInspector inspector) {
@@ -45,24 +48,26 @@
     // Only 2 of the 5 enum values are actually used:
     //   * REQUIRED: annotation for Test.field1
     //   * OPTIONAL: default value of WireField.label
-    // One more: values[]
-    assertEquals(3, classSubject.allFields().size());
+    // When generating class file the field values[] is also present as values() is kept.
+    assertEquals(
+        parameters.isCfRuntime() && defaultEnumValueInAnnotation ? 3 : 2,
+        classSubject.allFields().size());
+    // Methods <clinit>, <init> always present. values() present if generating class file.
+    assertEquals(
+        parameters.isCfRuntime() && defaultEnumValueInAnnotation ? 3 : 2,
+        classSubject.allMethods().size());
   }
 
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(classWireField(), classWireFieldLabel(), classTest())
+        .addProgramClassFileData(
+            classWireField(defaultEnumValueInAnnotation),
+            classWireFieldLabel(),
+            classTest(defaultEnumValueInAnnotation))
         .addProgramClasses(TestClass.class)
         .addKeepClassAndMembersRules(
-            "com.squareup.wire.WireField",
-            "com.squareup.demo.myapplication.Test")
-        // TODO(b/138156533): Need to trace enum values() if an instance is used as default value
-        //   for an annotation field.
-        .addKeepRules(StringUtils.lines(
-            "-keepclassmembers class com.squareup.wire.WireField$Label {",
-            "  public static *** values();",
-            "}"))
+            "com.squareup.wire.WireField", "com.squareup.demo.myapplication.Test")
         .addKeepMainRule(TestClass.class)
         .addKeepAttributes("*Annotation*")
         .setMinApi(parameters.getRuntime())
@@ -133,7 +138,7 @@
    )
 
   */
-  public static byte[] classWireField() throws Exception {
+  public static byte[] classWireField(boolean defaultEnumValueInAnnotation) throws Exception {
 
     ClassWriter classWriter = new ClassWriter(0);
     MethodVisitor methodVisitor;
@@ -220,9 +225,11 @@
               null,
               null);
       {
-        annotationVisitor0 = methodVisitor.visitAnnotationDefault();
-        annotationVisitor0.visitEnum(null, "Lcom/squareup/wire/WireField$Label;", "OPTIONAL");
-        annotationVisitor0.visitEnd();
+        if (defaultEnumValueInAnnotation) {
+          annotationVisitor0 = methodVisitor.visitAnnotationDefault();
+          annotationVisitor0.visitEnum(null, "Lcom/squareup/wire/WireField$Label;", "OPTIONAL");
+          annotationVisitor0.visitEnd();
+        }
       }
       methodVisitor.visitEnd();
     }
@@ -637,7 +644,7 @@
     return classWriter.toByteArray();
   }
 
-  public static byte[] classTest() throws Exception {
+  public static byte[] classTest(boolean defaultEnumValueInAnnotation) throws Exception {
 
     ClassWriter classWriter = new ClassWriter(0);
     FieldVisitor fieldVisitor;
@@ -691,6 +698,9 @@
         annotationVisitor0 = fieldVisitor.visitAnnotation("Lcom/squareup/wire/WireField;", true);
         annotationVisitor0.visit("tag", new Integer(1));
         annotationVisitor0.visit("adapter", "com.squareup.wire.ProtoAdapter#STRING");
+        if (!defaultEnumValueInAnnotation) {
+          annotationVisitor0.visitEnum("label", "Lcom/squareup/wire/WireField$Label;", "OPTIONAL");
+        }
         annotationVisitor0.visitEnd();
       }
       {