Enum unboxing: fix annotation with method parameters

Bug: 154950530
Change-Id: I68a044f507ccf7bb46f0884c341e48bc3c7e232f
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index 8f228b9..5e4eaed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -131,7 +131,7 @@
 
   private void removeEnumsInAnnotations() {
     for (DexProgramClass clazz : appView.appInfo().classes()) {
-      if (appView.appInfo().isSubtype(clazz.type, factory.annotationType)) {
+      if (clazz.isAnnotation()) {
         removeEnumsInAnnotation(clazz);
       }
     }
@@ -142,13 +142,15 @@
     // Each annotation value is represented by a virtual method.
     for (DexEncodedMethod method : clazz.virtualMethods()) {
       DexProto proto = method.method.proto;
-      assert proto.parameters.isEmpty();
-      DexType valueType = proto.returnType.toBaseType(appView.appInfo().dexItemFactory());
-      if (valueType.isClassType()
-          && enumToUnboxCandidates.containsKey(valueType)
-          && appView.appInfo().isSubtype(valueType, appView.appInfo().dexItemFactory().enumType)) {
-        enumUnboxer.reportFailure(valueType, Reason.ANNOTATION);
-        enumToUnboxCandidates.remove(valueType);
+      // There can be references to  enum unboxing candidates even if the parameter list is non
+      // empty. That is possible by injecting methods in the bytecode, but such methods are no
+      // different from other methods in the program, and can be rewritten by enum unboxing.
+      if (proto.parameters.isEmpty()) {
+        DexType valueType = proto.returnType.toBaseType(appView.appInfo().dexItemFactory());
+        if (enumToUnboxCandidates.containsKey(valueType)) {
+          enumUnboxer.reportFailure(valueType, Reason.ANNOTATION);
+          enumToUnboxCandidates.remove(valueType);
+        }
       }
     }
   }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
index 7cc0930..f9a5d80 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
@@ -5,9 +5,17 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import org.junit.Assume;
 import org.junit.Test;
@@ -40,17 +48,16 @@
         "The methods values and valueOf are required for reflection.",
         enumKeepRules.toString().equals("none"));
     testForR8(parameters.getBackend())
-        .addInnerClasses(AnnotationEnumUnboxingTest.class)
+        .addProgramFiles(getProgramClasses())
+        .addProgramClassFileData(getProgramClassesData())
+        .noMinification()
         .addKeepMainRule(Main.class)
         .addKeepRules(enumKeepRules.getKeepRule())
-        .addKeepRules(
-            "-keep @interface"
-                + " com.android.tools.r8.enumunboxing."
-                + "AnnotationEnumUnboxingTest$ClassAnnotationDefault"
-                + " {  }",
-            "-keepattributes *Annotation*")
+        .addKeepClassRules(ClassAnnotationDefault.class)
+        .addKeepRuntimeVisibleAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
+        .enableMergeAnnotations()
         .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
         .allowDiagnosticInfoMessages()
         .setMinApi(parameters.getApiLevel())
@@ -62,9 +69,31 @@
               assertEnumIsBoxed(
                   MyEnumArrayDefault.class, MyEnumArrayDefault.class.getSimpleName(), m);
               assertEnumIsBoxed(MyEnumArray.class, MyEnumArray.class.getSimpleName(), m);
+              assertEnumIsUnboxed(
+                  MyEnumRetMethod2.class, MyEnumRetMethod2.class.getSimpleName(), m);
+              assertEnumIsUnboxed(
+                  MyEnumParamMethod2.class, MyEnumParamMethod2.class.getSimpleName(), m);
             })
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("print", "1", "1", "1", "1", "1", "0", "0", "0");
+        .assertSuccessWithOutputLines("print", "1", "1", "1", "1", "1", "0", "0", "0", "0");
+  }
+
+  private Collection<byte[]> getProgramClassesData() throws IOException {
+    return Collections.singletonList(transformAnnotationToBe());
+  }
+
+  private Collection<Path> getProgramClasses() throws IOException {
+    Collection<Path> inputs =
+        ToolHelper.getClassFilesForInnerClasses(
+            Collections.singletonList(AnnotationEnumUnboxingTest.class));
+    inputs.removeIf(p -> p.toString().contains("ClassAnnotationToBe.class"));
+    return inputs;
+  }
+
+  private byte[] transformAnnotationToBe() throws IOException {
+    return transformer(ClassAnnotationToBe.class)
+        .setAccessFlags(ClassAccessFlags::setAnnotation)
+        .transform();
   }
 
   @Retention(RetentionPolicy.RUNTIME)
@@ -78,6 +107,27 @@
     MyEnumArray[] myEnumArray();
   }
 
+  // This will be transformed into an annotation.
+  @NeverMerge
+  @NeverClassInline
+  interface ClassAnnotationToBe {
+    @NeverInline
+    @NeverPropagateValue
+    default MyEnumRetMethod2 enumMethod(MyEnumParamMethod2 param) {
+      return param == MyEnumParamMethod2.A ? MyEnumRetMethod2.A : MyEnumRetMethod2.B;
+    }
+  }
+
+  enum MyEnumParamMethod2 {
+    A,
+    B
+  }
+
+  enum MyEnumRetMethod2 {
+    A,
+    B
+  }
+
   enum MyEnumDefault {
     A,
     B
@@ -111,6 +161,8 @@
     }
   }
 
+  static class ClassAnnotationToBeSub implements ClassAnnotationToBe {}
+
   static class Main {
     public static void main(String[] args) {
       new ClassDefault().print();
@@ -124,6 +176,8 @@
       System.out.println(annotation.myEnumArray()[0].ordinal());
       System.out.println(annotation.myEnumArrayDefault()[0].ordinal());
       System.out.println(annotation.myEnumDefault().ordinal());
+      // We need two classes toa void merging.
+      System.out.println(new ClassAnnotationToBeSub().enumMethod(MyEnumParamMethod2.A).ordinal());
     }
   }
 }