Introduce a "reported set" and prepare to split reasons from witness.

Bug: 120959039
Change-Id: I512ae6e9de917bdfa90eef8202049786885697b2
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 fd9a75f..1350e17 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -176,7 +176,7 @@
    * Set of types that are mentioned in the program. We at least need an empty abstract class item
    * for these.
    */
-  private final SetWithReason<DexProgramClass> liveTypes;
+  private final SetWithReportedReason<DexProgramClass> liveTypes;
 
   /** Set of live types defined in the library and classpath. Used to avoid duplicate tracing. */
   private final Set<DexClass> liveNonProgramTypes = Sets.newIdentityHashSet();
@@ -304,7 +304,7 @@
       registerAnalysis(new ProtoEnqueuerExtension(appView));
     }
 
-    liveTypes = new SetWithReason<>(graphReporter::registerClass);
+    liveTypes = new SetWithReportedReason<>();
     liveAnnotations = new SetWithReason<>(graphReporter::registerAnnotation);
     instantiatedTypes = new SetWithReason<>(graphReporter::registerClass);
     targetedMethods = new SetWithReason<>(graphReporter::registerMethod);
@@ -451,14 +451,14 @@
     pinnedItems.add(item.toReference());
   }
 
-  private void markInterfaceAsInstantiated(DexProgramClass clazz, KeepReason reason) {
+  private void markInterfaceAsInstantiated(DexProgramClass clazz, KeepReasonWitness witness) {
     assert clazz.isInterface() && !clazz.accessFlags.isAnnotation();
 
-    if (!instantiatedInterfaceTypes.add(clazz, reason)) {
+    if (!instantiatedInterfaceTypes.add(clazz, witness)) {
       return;
     }
     populateInstantiatedTypesCache(clazz);
-    markTypeAsLive(clazz, reason);
+    markTypeAsLive(clazz, witness);
   }
 
   private void enqueueFirstNonSerializableClassInitializer(
@@ -774,7 +774,7 @@
       DexProgramClass clazz = getProgramClassOrNull(type);
       if (clazz != null) {
         if (clazz.isInterface()) {
-          markTypeAsLive(clazz, keepReason);
+          markTypeAsLive(clazz, graphReporter.registerClass(clazz, keepReason));
         } else {
           markInstantiated(clazz, keepReason);
         }
@@ -911,7 +911,7 @@
         if (clazz != null) {
           KeepReason reason = KeepReason.methodHandleReferencedIn(currentMethod);
           if (clazz.isInterface() && !clazz.accessFlags.isAnnotation()) {
-            markInterfaceAsInstantiated(clazz, reason);
+            markInterfaceAsInstantiated(clazz, graphReporter.registerClass(clazz, reason));
           } else {
             markInstantiated(clazz, reason);
           }
@@ -1083,10 +1083,10 @@
     markTypeAsLive(
         holder,
         scopedMethodsForLiveTypes.computeIfAbsent(type, ignore -> new ScopedDexMethodSet()),
-        reason);
+        graphReporter.registerClass(holder, reason));
   }
 
-  private void markTypeAsLive(DexType type, Function<DexProgramClass, KeepReason> reason) {
+  private void markTypeAsLive(DexType type, Function<DexProgramClass, KeepReasonWitness> reason) {
     if (type.isArrayType()) {
       markTypeAsLive(type.toBaseType(appView.dexItemFactory()), reason);
       return;
@@ -1105,16 +1105,16 @@
         reason.apply(holder));
   }
 
-  private void markTypeAsLive(DexProgramClass clazz, KeepReason reason) {
+  private void markTypeAsLive(DexProgramClass clazz, KeepReasonWitness witness) {
     markTypeAsLive(
         clazz,
         scopedMethodsForLiveTypes.computeIfAbsent(clazz.type, ignore -> new ScopedDexMethodSet()),
-        reason);
+        witness);
   }
 
   private void markTypeAsLive(
-      DexProgramClass holder, ScopedDexMethodSet seen, KeepReason reasonForType) {
-    if (!liveTypes.add(holder, reasonForType)) {
+      DexProgramClass holder, ScopedDexMethodSet seen, KeepReasonWitness witness) {
+    if (!liveTypes.add(holder, witness)) {
       return;
     }
 
@@ -1432,7 +1432,7 @@
       Log.verbose(getClass(), "Class `%s` is instantiated, processing...", clazz);
     }
     // This class becomes live, so it and all its supertypes become live types.
-    markTypeAsLive(clazz, reason);
+    markTypeAsLive(clazz, graphReporter.registerClass(clazz, reason));
     // For all methods of the class, if we have seen a call, mark the method live.
     // We only do this for virtual calls, as the other ones will be done directly.
     transitionMethodsForInstantiatedClass(clazz);
@@ -2489,12 +2489,13 @@
     }
   }
 
-  private void markClassAsInstantiatedWithCompatRule(DexProgramClass clazz, KeepReason reason) {
+  private void markClassAsInstantiatedWithCompatRule(
+      DexProgramClass clazz, KeepReasonWitness witness) {
     if (clazz.isInterface() && !clazz.accessFlags.isAnnotation()) {
-      markInterfaceAsInstantiated(clazz, reason);
+      markInterfaceAsInstantiated(clazz, witness);
       return;
     }
-    workList.enqueueMarkInstantiatedAction(clazz, reason);
+    workList.enqueueMarkInstantiatedAction(clazz, witness);
     if (clazz.hasDefaultInitializer()) {
       DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
       workList.enqueueMarkReachableDirectAction(
@@ -2845,6 +2846,24 @@
     }
   }
 
+  private static class SetWithReportedReason<T> {
+
+    private final Set<T> items = Sets.newIdentityHashSet();
+
+    boolean add(T item, KeepReasonWitness witness) {
+      assert witness != null;
+      return items.add(item);
+    }
+
+    boolean contains(T item) {
+      return items.contains(item);
+    }
+
+    Set<T> getItems() {
+      return Collections.unmodifiableSet(items);
+    }
+  }
+
   private static class SetWithReason<T> {
 
     private final Set<T> items = Sets.newIdentityHashSet();
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index e87abf0..5dc405e 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -175,7 +175,7 @@
           getMethodGraphNode(defaultInitializer.method),
           EdgeKind.CompatibilityRule);
     }
-    return KeepReasonWitness.COMPAT_INSTANCE;
+    return KeepReasonWitness.INSTANCE;
   }
 
   public KeepReasonWitness reportCompatKeepMethod(DexProgramClass holder, DexEncodedMethod method) {
@@ -184,10 +184,10 @@
     // The rule is stating that if the method is targeted it is live. Since such an edge does
     // not contribute to additional information in the kept graph as it stands (no distinction
     // of targeted vs live edges), there is little point in emitting it.
-    return KeepReasonWitness.COMPAT_INSTANCE;
+    return KeepReasonWitness.INSTANCE;
   }
 
-  public KeepReason reportCompatInstantiated(
+  public KeepReasonWitness reportCompatInstantiated(
       DexProgramClass instantiated, DexEncodedMethod method) {
     if (keptGraphConsumer != null) {
       reportEdge(
@@ -195,7 +195,7 @@
           getClassGraphNode(instantiated.type),
           EdgeKind.CompatibilityRule);
     }
-    return KeepReasonWitness.COMPAT_INSTANCE;
+    return KeepReasonWitness.INSTANCE;
   }
 
   public KeepReasonWitness reportClassReferencedFrom(
@@ -240,7 +240,7 @@
     return KeepReasonWitness.INSTANCE;
   }
 
-  public KeepReason reportCompanionClass(DexProgramClass iface, DexProgramClass companion) {
+  public KeepReasonWitness reportCompanionClass(DexProgramClass iface, DexProgramClass companion) {
     assert iface.isInterface();
     assert InterfaceMethodRewriter.isCompanionClassType(companion.type);
     if (keptGraphConsumer == null) {
@@ -250,7 +250,7 @@
         getClassGraphNode(iface.type), getClassGraphNode(companion.type), EdgeKind.CompanionClass);
   }
 
-  public KeepReason reportCompanionMethod(
+  public KeepReasonWitness reportCompanionMethod(
       DexEncodedMethod definition, DexEncodedMethod implementation) {
     assert InterfaceMethodRewriter.isCompanionClassType(implementation.method.holder);
     if (keptGraphConsumer == null) {
@@ -276,13 +276,6 @@
   public static class KeepReasonWitness extends KeepReason {
 
     private static KeepReasonWitness INSTANCE = new KeepReasonWitness();
-    private static KeepReasonWitness COMPAT_INSTANCE =
-        new KeepReasonWitness() {
-          @Override
-          public boolean isDueToProguardCompatibility() {
-            return true;
-          }
-        };
 
     private KeepReasonWitness() {
       // Only the reporter may create instances.
@@ -301,7 +294,7 @@
 
   private boolean skipReporting(KeepReason reason) {
     assert reason != null;
-    if (reason == KeepReasonWitness.INSTANCE || reason == KeepReasonWitness.COMPAT_INSTANCE) {
+    if (reason == KeepReasonWitness.INSTANCE) {
       return true;
     }
     assert getSourceNode(reason) != null;
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index 5f5d772..9dbb65e 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -64,10 +64,6 @@
     return false;
   }
 
-  public boolean isDueToProguardCompatibility() {
-    return false;
-  }
-
   public boolean isInstantiatedIn() {
     return false;
   }