Reapply "Extend subtyping info after desugaring"

This reverts commit 25bca49383337132683e5a07778aa1cecf2bf5aa.

Bug: b/188395655
Change-Id: I1363c310a3c1c5a32a694766e0e8af20cc86329f
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 3293b0b..40f7230 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -1190,6 +1190,11 @@
       SubtypingInfo subtypingInfo,
       List<KeepDeclaration> keepDeclarations)
       throws ExecutionException {
+    timing.begin("Update subtyping info");
+    subtypingInfo.unsetTypeInfo();
+    subtypingInfo.update(appView);
+    assert subtypingInfo.verifyUpToDate(appView);
+    timing.end();
     timing.begin("Set up enqueuer");
     Enqueuer enqueuer =
         EnqueuerFactory.createForInitialTreeShaking(
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index 1d27755..523caf6 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -4,7 +4,9 @@
 package com.android.tools.r8.graph;
 
 import static com.android.tools.r8.graph.DexApplication.classesWithDeterministicOrder;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
 
+import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -33,7 +35,7 @@
   // Map from types to their subtypes.
   private final Map<DexType, Set<DexType>> subtypeMap;
 
-  private final Map<DexType, TypeInfo> typeInfo;
+  private Map<DexType, TypeInfo> typeInfo;
 
   private final DexDefinitionSupplier definitionSupplier;
   private final DexItemFactory factory;
@@ -48,16 +50,38 @@
     factory = definitionSupplier.dexItemFactory();
   }
 
-  public static SubtypingInfo create(AppView<? extends AppInfoWithClassHierarchy> appView) {
-    return create(appView.appInfo());
+  public void update(AppView<? extends AppInfoWithClassHierarchy> appView) {
+    assert typeInfo == null : "Extending typeInfo not implemented";
+    if (!appView.getSyntheticItems().hasPendingSyntheticClasses()) {
+      return;
+    }
+    WorkList<DexType> worklist = WorkList.newIdentityWorkList();
+    for (DexClass clazz : appView.getSyntheticItems().getAllPendingSyntheticClasses()) {
+      worklist.addIfNotSeen(clazz.allImmediateSupertypes());
+      worklist.process(
+          supertype -> {
+            DexClass superclass = appView.definitionFor(supertype);
+            if (superclass == null) {
+              return;
+            }
+            subtypeMap.computeIfAbsent(supertype, ignoreKey(HashSet::new)).add(clazz.getType());
+            worklist.addIfNotSeen(superclass.allImmediateSupertypes());
+          });
+      worklist.clearSeen();
+    }
   }
 
-  public static SubtypingInfo create(AppInfoWithClassHierarchy appInfo) {
-    DirectMappedDexApplication directApp = appInfo.app().asDirect();
-    return create(
-        Iterables.concat(
-            directApp.programClasses(), directApp.classpathClasses(), directApp.libraryClasses()),
-        appInfo);
+  public SubtypingInfo unsetTypeInfo() {
+    typeInfo = null;
+    return this;
+  }
+
+  public static SubtypingInfo create(AppView<? extends AppInfoWithClassHierarchy> appView) {
+    AppInfoWithClassHierarchy appInfo = appView.appInfo();
+    DirectMappedDexApplication app = appInfo.app().asDirect();
+    Iterable<DexClass> classes =
+        Iterables.concat(app.programClasses(), app.classpathClasses(), app.libraryClasses());
+    return create(classes, appInfo);
   }
 
   public static SubtypingInfo create(
@@ -134,7 +158,6 @@
     for (DexClass clazz : classes) {
       populateAllSuperTypes(map, typeInfo, clazz.type, clazz, definitionSupplier);
     }
-    map.replaceAll((k, v) -> ImmutableSet.copyOf(v));
     assert validateLevelsAreCorrect(typeInfo, definitionSupplier);
   }
 
@@ -233,6 +256,38 @@
     return classesWithDeterministicOrder(interfaces);
   }
 
+  public boolean verifyUpToDate(AppView<AppInfoWithClassHierarchy> appView) {
+    DirectMappedDexApplication app = appView.app().asDirect();
+    Iterable<DexClass> classes =
+        Iterables.concat(app.programClasses(), app.classpathClasses(), app.libraryClasses());
+    for (DexClass clazz : classes) {
+      assert verifyUpToDate(appView, clazz);
+    }
+    // This does not check that the `typeInfo` is up-to-date.
+    assert typeInfo == null;
+    return true;
+  }
+
+  private boolean verifyUpToDate(AppView<AppInfoWithClassHierarchy> appView, DexClass clazz) {
+    WorkList<DexType> worklist = WorkList.newIdentityWorkList(clazz.allImmediateSupertypes());
+    worklist.process(
+        supertype -> {
+          DexClass superclass = appView.definitionFor(supertype);
+          if (superclass == null) {
+            return;
+          }
+          assert subtypes(supertype).contains(clazz.getType())
+                  || (clazz.isLibraryClass()
+                      && appView.definitionFor(clazz.getType()).isProgramClass())
+              : "Expected subtypes("
+                  + supertype.getTypeName()
+                  + ") to include "
+                  + clazz.getTypeName();
+          worklist.addIfNotSeen(superclass.allImmediateSupertypes());
+        });
+    return true;
+  }
+
   private static class TypeInfo {
 
     private final DexType type;
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 4d0f480..64690e7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4212,9 +4212,10 @@
     }
 
     // Commit the pending synthetics and recompute subtypes.
+    subtypingInfo.update(appView);
     appInfo = timing.time("Rebuild AppInfo", () -> appInfo.rebuildWithClassHierarchy(app -> app));
     appView.setAppInfo(appInfo);
-    subtypingInfo = timing.time("Create SubtypingInfo", () -> SubtypingInfo.create(appView));
+    assert subtypingInfo.verifyUpToDate(appView);
 
     // Finally once all synthesized items "exist" it is now safe to continue tracing. The new work
     // items are enqueued and the fixed point will continue once this subroutine returns.
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 6a61237..bec8b29 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -754,7 +754,7 @@
                           resolutionMethod
                               .getDefinition()
                               .toForwardingMethod(originalClazz, appView)));
-          assert methodToKeepReference.equals(methodToKeep.getReference());
+          assert methodToKeepReference.isIdenticalTo(methodToKeep.getReference());
         } else {
           methodToKeep = resolutionMethod;
         }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index e15699a..b5d4585 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -205,6 +205,14 @@
       }
       return Collections.unmodifiableList(allPending);
     }
+
+    Collection<DexClass> getAllClasses() {
+      List<DexClass> allPending = new ArrayList<>(definitions.size());
+      for (SyntheticDefinition<?, ?, ?> item : definitions.values()) {
+        allPending.add(item.getHolder());
+      }
+      return Collections.unmodifiableList(allPending);
+    }
   }
 
   private final State state;
@@ -443,6 +451,10 @@
     return pending.getAllProgramClasses();
   }
 
+  public Collection<DexClass> getAllPendingSyntheticClasses() {
+    return pending.getAllClasses();
+  }
+
   public boolean isCommittedSynthetic(DexType type) {
     return committed.containsType(type);
   }
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java
index a7c4dc6..94b4f19 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -969,18 +969,21 @@
     // Run the tree shaker to compute an instance of AppInfoWithLiveness.
     ExecutorService executor = Executors.newSingleThreadExecutor();
     ProfileCollectionAdditions profileCollectionAdditions = ProfileCollectionAdditions.nop();
-    SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
     RootSet rootSet =
         RootSet.builder(
                 appView,
                 profileCollectionAdditions,
-                subtypingInfo,
+                SubtypingInfo.create(appView),
                 appView.options().getProguardConfiguration().getRules())
             .build(executor);
     appView.setRootSet(rootSet);
+    appView.rebuildAppInfo();
     EnqueuerResult enqueuerResult =
         EnqueuerFactory.createForInitialTreeShaking(
-                appView, profileCollectionAdditions, executor, subtypingInfo)
+                appView,
+                profileCollectionAdditions,
+                executor,
+                SubtypingInfo.create(appView).unsetTypeInfo())
             .traceApplication(rootSet, executor, Timing.empty());
     executor.shutdown();
     // We do not run the tree pruner to ensure that the hierarchy is as designed and not modified