Assure synthesized class is registered in TypeInfo.

Bug: 143686595
Change-Id: I58d1df683b12238c944d56140f89b8a4cae6aaa4
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 d569852..139a285 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -641,11 +641,6 @@
     return null;
   }
 
-  public void registerNewType(DexType newType, DexType superType) {
-    // We do not track subtyping relationships in the basic AppInfo. So do nothing.
-    assert checkIfObsolete();
-  }
-
   public boolean isInMainDexList(DexType type) {
     assert checkIfObsolete();
     return app.mainDexList.contains(type);
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 faa25c2..fa4cea3 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -3,11 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_GROUP_CLASS_NAME_PREFIX;
+
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
 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.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -48,7 +51,7 @@
     // Caching what interfaces this type is implementing. This includes super-interface hierarchy.
     Set<DexType> implementedInterfaces = null;
 
-    public TypeInfo(DexType type) {
+    TypeInfo(DexType type) {
       this.type = type;
     }
 
@@ -78,7 +81,7 @@
       }
     }
 
-    public synchronized void addDirectSubtype(TypeInfo subtypeInfo) {
+    synchronized void addDirectSubtype(TypeInfo subtypeInfo) {
       assert hierarchyLevel != UNKNOWN_LEVEL;
       ensureDirectSubTypeSet();
       directSubtypes.add(subtypeInfo.type);
@@ -149,6 +152,28 @@
   @Override
   public void addSynthesizedClass(DexProgramClass synthesizedClass) {
     super.addSynthesizedClass(synthesizedClass);
+    // Register synthesized type, which has two side effects:
+    //   1) Set the hierarchy level of synthesized type based on that of its super type,
+    //   2) Register the synthesized type as a subtype of the supertype.
+    //
+    // The first one makes method resolutions on that synthesized class free from assertion errors
+    // about unknown hierarchy level.
+    //
+    // For the second one, note that such addition is synchronized, but the retrieval of direct
+    // subtypes isn't. Thus, there is a chance of race conditions: utils that check/iterate direct
+    // subtypes, e.g., allImmediateSubtypes, hasSubTypes, etc., may not be able to see this new
+    // synthesized class. However, in practice, this would be okay because, in most cases,
+    // synthesized class's super type is Object, which in general has other subtypes in any way.
+    // Also, iterating all subtypes of Object usually happens before/after IR processing, i.e., as
+    // part of structural changes, such as bottom-up traversal to collect all method signatures,
+    // which are free from such race conditions. Another exceptional case is synthesized classes
+    // whose synthesis is isolated from IR processing. For example, lambda group class that merges
+    // lambdas with the same interface are synthesized/finalized even after post processing of IRs.
+    assert synthesizedClass.superType == dexItemFactory().objectType
+            || synthesizedClass.type.toString().contains(LAMBDA_GROUP_CLASS_NAME_PREFIX)
+        : "Make sure retrieval and iteration of sub types of `" + synthesizedClass.superType
+            + "` is guaranteed to be thread safe and able to see `" + synthesizedClass + "`";
+    registerNewType(synthesizedClass.type, synthesizedClass.superType);
 
     // TODO(b/129458850): Remove when we no longer synthesize classes on-the-fly.
     Set<DexType> visited = SetUtils.newIdentityHashSet(synthesizedClass.allImmediateSupertypes());
@@ -217,8 +242,11 @@
     return typeInfo.computeIfAbsent(type, TypeInfo::new);
   }
 
-  private void populateAllSuperTypes(Map<DexType, Set<DexType>> map, DexType holder,
-      DexClass baseClass, Function<DexType, DexClass> definitions) {
+  private void populateAllSuperTypes(
+      Map<DexType, Set<DexType>> map,
+      DexType holder,
+      DexClass baseClass,
+      Function<DexType, DexClass> definitions) {
     DexClass holderClass = definitions.apply(holder);
     // Skip if no corresponding class is found.
     if (holderClass != null) {
@@ -435,13 +463,17 @@
             || bootstrapMethod.asMethod() == dexItemFactory().stringConcatMethod);
   }
 
-  @Override
-  public void registerNewType(DexType newType, DexType superType) {
+  private void registerNewType(DexType newType, DexType superType) {
     assert checkIfObsolete();
     // Register the relationship between this type and its superType.
     getTypeInfo(superType).addDirectSubtype(getTypeInfo(newType));
   }
 
+  @VisibleForTesting
+  public void registerNewTypeForTesting(DexType newType, DexType superType) {
+    registerNewType(newType, superType);
+  }
+
   @Override
   public boolean hasSubtyping() {
     assert checkIfObsolete();
@@ -495,7 +527,7 @@
 
   // Depending on optimizations, conservative answer of subtype relation may vary.
   // Pass different `orElse` in that case.
-  public boolean isStrictSubtypeOf(DexType subtype, DexType supertype, boolean orElse) {
+  private boolean isStrictSubtypeOf(DexType subtype, DexType supertype, boolean orElse) {
     if (subtype == supertype) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 8a93298..9935044 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -939,8 +939,6 @@
       count++;
       result = appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(name));
     } while (appView.definitionFor(result) != null);
-    // Register the newly generated type in the subtyping hierarchy, if we have one.
-    appView.appInfo().registerNewType(result, appView.dexItemFactory().objectType);
     return result;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 40932cb..9998c94 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -105,9 +105,6 @@
                 lambdaClassType,
                 factory.createProto(lambdaClassType, descriptor.captures.values),
                 rewriter.createInstanceMethodName);
-
-    // We have to register this new class as a subtype of object.
-    rewriter.converter.appView.appInfo().registerNewType(type, factory.objectType);
   }
 
   // Generate unique lambda class type for lambda descriptor and instantiation point context.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index e39c21b..2ac3abd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1353,7 +1353,6 @@
     // No need to sort the direct methods as they are generated in sorted order.
 
     // Build the outliner class.
-    DexType superType = appView.dexItemFactory().createType("Ljava/lang/Object;");
     DexTypeList interfaces = DexTypeList.empty();
     DexString sourceFile = appView.dexItemFactory().createString("outline");
     ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
@@ -1365,7 +1364,7 @@
         null,
         new SynthesizedOrigin("outlining", getClass()),
         accessFlags,
-        superType,
+        appView.dexItemFactory().objectType,
         interfaces,
         sourceFile,
         null,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 57edf39..a165998 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -384,9 +384,6 @@
       group.compact();
       DexProgramClass lambdaGroupClass = group.synthesizeClass(appView.options());
       result.put(group, lambdaGroupClass);
-
-      // We have to register this new class as a subtype of object.
-      appView.appInfo().registerNewType(lambdaGroupClass.type, lambdaGroupClass.superType);
     }
     return result;
   }
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
index 42908d2..3c47534 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
@@ -536,7 +536,7 @@
   @Test
   public void testSelfOrderWithoutSubtypingInfo() {
     DexType type = factory.createType("Lmy/Type;");
-    appView.withSubtyping().appInfo().registerNewType(type, factory.objectType);
+    appView.withSubtyping().appInfo().registerNewTypeForTesting(type, factory.objectType);
     TypeLatticeElement nonNullType = fromDexType(type, Nullability.definitelyNotNull(), appView);
     ReferenceTypeLatticeElement nullableType =
         nonNullType.asReferenceTypeLatticeElement().getOrCreateVariant(Nullability.maybeNull());