Fix subtyping queries for synthesized classes

Change-Id: Ide408a08aa3030e962dada9ff2bcb0f6a3d0d2a7
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 047c498..e2ad67e 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -26,8 +26,7 @@
       new ConcurrentHashMap<>();
   // For some optimizations, e.g. optimizing synthetic classes, we may need to resolve the current
   // class being optimized.
-  private final ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses =
-      new ConcurrentHashMap<>();
+  final ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses = new ConcurrentHashMap<>();
 
   // Set when a new AppInfo replaces a previous one. All public methods should verify that the
   // current instance is not obsolete, to ensure that we almost use the most recent AppInfo.
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 82dbe10..2e37930 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.SetUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -19,6 +20,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -114,6 +116,10 @@
   // Map from types to their subtypes.
   private final Map<DexType, ImmutableSet<DexType>> subtypeMap = new IdentityHashMap<>();
 
+  // Map from synthesized classes to their supertypes.
+  private final Map<DexType, ImmutableSet<DexType>> supertypesForSynthesizedClasses =
+      new ConcurrentHashMap<>();
+
   // Map from types to their subtyping information.
   private final Map<DexType, TypeInfo> typeInfo;
 
@@ -132,6 +138,39 @@
     assert app() instanceof DirectMappedDexApplication;
   }
 
+  @Override
+  public void addSynthesizedClass(DexProgramClass synthesizedClass) {
+    super.addSynthesizedClass(synthesizedClass);
+
+    // TODO(b/129458850): Remove when we no longer synthesize classes on-the-fly.
+    Set<DexType> visited = SetUtils.newIdentityHashSet(synthesizedClass.allImmediateSupertypes());
+    Deque<DexType> worklist = new ArrayDeque<>(visited);
+    while (!worklist.isEmpty()) {
+      DexType type = worklist.removeFirst();
+      assert visited.contains(type);
+
+      DexClass clazz = definitionFor(type);
+      if (clazz == null) {
+        continue;
+      }
+
+      for (DexType supertype : clazz.allImmediateSupertypes()) {
+        if (visited.add(supertype)) {
+          worklist.addLast(supertype);
+        }
+      }
+    }
+    if (!visited.isEmpty()) {
+      supertypesForSynthesizedClasses.put(synthesizedClass.type, ImmutableSet.copyOf(visited));
+    }
+  }
+
+  private boolean isSynthesizedClassStrictSubtypeOf(DexType synthesizedClass, DexType supertype) {
+    Set<DexType> supertypesOfSynthesizedClass =
+        supertypesForSynthesizedClasses.get(synthesizedClass);
+    return supertypesOfSynthesizedClass != null && supertypesOfSynthesizedClass.contains(supertype);
+  }
+
   private DirectMappedDexApplication getDirectApplication() {
     // TODO(herhut): Remove need for cast.
     return (DirectMappedDexApplication) app();
@@ -426,12 +465,24 @@
 
   @Override
   public boolean isSubtype(DexType subtype, DexType supertype) {
-    return subtype == supertype || isStrictSubtypeOf(subtype, supertype);
+    if (subtype == supertype || isStrictSubtypeOf(subtype, supertype)) {
+      return true;
+    }
+    if (synthesizedClasses.containsKey(subtype)) {
+      return isSynthesizedClassStrictSubtypeOf(subtype, supertype);
+    }
+    return false;
   }
 
   public boolean isStrictSubtypeOf(DexType subtype, DexType supertype) {
     // For all erroneous cases, saying `no`---not a strict subtype---is conservative.
-    return isStrictSubtypeOf(subtype, supertype, false);
+    if (isStrictSubtypeOf(subtype, supertype, false)) {
+      return true;
+    }
+    if (synthesizedClasses.containsKey(subtype)) {
+      return isSynthesizedClassStrictSubtypeOf(subtype, supertype);
+    }
+    return false;
   }
 
   // Depending on optimizations, conservative answer of subtype relation may vary.