Rewrite type elements after redundant interface removal

Bug: 183734568
Change-Id: Ic2354d7d15f2794bea112b4ed7ce74379edd4913
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a6f1b9c..546fad4 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -374,7 +374,7 @@
               shrinker -> shrinker.run(Mode.INITIAL_TREE_SHAKING));
 
           TreePruner pruner = new TreePruner(appViewWithLiveness);
-          DirectMappedDexApplication prunedApp = pruner.run();
+          DirectMappedDexApplication prunedApp = pruner.run(executorService);
 
           // Recompute the subtyping information.
           Set<DexType> removedClasses = pruner.getRemovedClasses();
@@ -609,7 +609,7 @@
                     DefaultTreePrunerConfiguration.getInstance());
 
             TreePruner pruner = new TreePruner(appViewWithLiveness, treePrunerConfiguration);
-            DirectMappedDexApplication application = pruner.run();
+            DirectMappedDexApplication application = pruner.run(executorService);
             Set<DexType> removedClasses = pruner.getRemovedClasses();
 
             if (options.usageInformationConsumer != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 412a66d..6d57a75 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2568,12 +2568,15 @@
               if (type.isClassType()) {
                 if (!appView.enableWholeProgramOptimizations()) {
                   // Don't reason at the level of interfaces in D8.
-                  return ClassTypeElement.create(type, nullability, InterfaceCollection.empty());
+                  return ClassTypeElement.createForD8(type, nullability);
                 }
                 assert appView.appInfo().hasClassHierarchy();
                 if (appView.isInterface(type).isTrue()) {
                   return ClassTypeElement.create(
-                      objectType, nullability, InterfaceCollection.singleton(type));
+                      objectType,
+                      nullability,
+                      appView.withClassHierarchy(),
+                      InterfaceCollection.singleton(type));
                 }
                 // In theory, `interfaces` is the least upper bound of implemented interfaces.
                 // It is expensive to walk through type hierarchy; collect implemented interfaces;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
index caca28e..78332d5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Function;
 
 public class ArrayTypeElement extends ReferenceTypeElement {
@@ -134,10 +135,12 @@
 
   @Override
   public ArrayTypeElement fixupClassTypeReferences(
-      AppView<? extends AppInfoWithClassHierarchy> appView, Function<DexType, DexType> mapping) {
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      Function<DexType, DexType> mapping,
+      Set<DexType> prunedTypes) {
     if (memberTypeLattice.isReferenceType()) {
       TypeElement substitutedMemberType =
-          memberTypeLattice.fixupClassTypeReferences(appView, mapping);
+          memberTypeLattice.fixupClassTypeReferences(appView, mapping, prunedTypes);
       if (substitutedMemberType != memberTypeLattice) {
         return ArrayTypeElement.create(substitutedMemberType, nullability);
       }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index e1a924c..083c27f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -23,6 +23,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Queue;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -37,11 +38,16 @@
   private final DexType type;
 
   public static ClassTypeElement create(
-      DexType classType, Nullability nullability, InterfaceCollection interfaces) {
+      DexType classType,
+      Nullability nullability,
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      InterfaceCollection interfaces) {
+    assert appView != null;
+    assert appView.enableWholeProgramOptimizations();
     assert interfaces != null;
     return NullabilityVariants.create(
         nullability,
-        (variants) -> new ClassTypeElement(classType, nullability, interfaces, variants, null));
+        variants -> new ClassTypeElement(classType, nullability, interfaces, variants, appView));
   }
 
   public static ClassTypeElement create(
@@ -49,9 +55,18 @@
       Nullability nullability,
       AppView<? extends AppInfoWithClassHierarchy> appView) {
     assert appView != null;
+    assert appView.enableWholeProgramOptimizations();
     return NullabilityVariants.create(
         nullability,
-        (variants) -> new ClassTypeElement(classType, nullability, null, variants, appView));
+        variants -> new ClassTypeElement(classType, nullability, null, variants, appView));
+  }
+
+  public static ClassTypeElement createForD8(DexType classType, Nullability nullability) {
+    return NullabilityVariants.create(
+        nullability,
+        variants ->
+            new ClassTypeElement(
+                classType, nullability, InterfaceCollection.empty(), variants, null));
   }
 
   private ClassTypeElement(
@@ -61,11 +76,13 @@
       NullabilityVariants<ClassTypeElement> variants,
       AppView<? extends AppInfoWithClassHierarchy> appView) {
     super(nullability);
+    assert appView != null
+        ? appView.enableWholeProgramOptimizations()
+        : (interfaces != null && interfaces.isEmpty());
     assert classType.isClassType();
-    assert interfaces != null || appView != null;
-    type = classType;
+    this.type = classType;
     this.appView = appView;
-    lazyInterfaces = interfaces;
+    this.lazyInterfaces = interfaces;
     this.variants = variants;
   }
 
@@ -75,12 +92,13 @@
 
   public InterfaceCollection getInterfaces() {
     if (lazyInterfaces == null) {
+      // Class type interfaces are cached on DexItemFactory.
       assert appView != null;
-      lazyInterfaces =
-          appView.dexItemFactory()
-              .getOrComputeLeastUpperBoundOfImplementedInterfaces(type, appView);
+      assert appView.enableWholeProgramOptimizations();
+      return appView
+          .dexItemFactory()
+          .getOrComputeLeastUpperBoundOfImplementedInterfaces(type, appView);
     }
-    assert lazyInterfaces != null;
     return lazyInterfaces;
   }
 
@@ -90,11 +108,6 @@
     return new ClassTypeElement(type, nullability, lazyInterfaces, variants, appView);
   }
 
-  public boolean isRelatedTo(ClassTypeElement other, AppView<?> appView) {
-    return lessThanOrEqualUpToNullability(other, appView)
-        || other.lessThanOrEqualUpToNullability(this, appView);
-  }
-
   @Override
   public ClassTypeElement getOrCreateVariant(Nullability nullability) {
     ClassTypeElement variant = variants.get(nullability);
@@ -156,17 +169,21 @@
 
   @Override
   public TypeElement fixupClassTypeReferences(
-      AppView<? extends AppInfoWithClassHierarchy> appView, Function<DexType, DexType> mapping) {
+      AppView<? extends AppInfoWithClassHierarchy> ignore,
+      Function<DexType, DexType> mapping,
+      Set<DexType> prunedTypes) {
+    assert appView != null;
+    assert appView.enableWholeProgramOptimizations();
+
     DexType mappedType = mapping.apply(type);
     if (mappedType.isPrimitiveType()) {
       return PrimitiveTypeElement.fromDexType(mappedType, false);
     }
-    if (mappedType != type) {
-      return create(mappedType, nullability, appView);
-    }
-    // If the mapped type is not object and no computation of interfaces, we can return early.
-    if (mappedType != appView.dexItemFactory().objectType && lazyInterfaces == null) {
-      return this;
+
+    // If there are no interfaces, then just return the mapped type element. Otherwise, the list of
+    // interfaces require rewriting.
+    if (lazyInterfaces == null || lazyInterfaces.isEmpty()) {
+      return mappedType == type ? this : create(mappedType, nullability, appView);
     }
 
     // For most types there will not have been a change thus we iterate without allocating a new
@@ -176,6 +193,9 @@
     getInterfaces()
         .forEach(
             (iface, isKnown) -> {
+              if (prunedTypes.contains(iface)) {
+                return;
+              }
               DexType substitutedType = mapping.apply(iface);
               if (iface != substitutedType) {
                 hasChangedInterfaces.set();
@@ -196,7 +216,7 @@
     if (hasChangedInterfaces.get()) {
       if (interfaceToClassChange.isSet()) {
         assert !interfaceToClassChange.get().isInterface();
-        assert type == appView.dexItemFactory().objectType;
+        assert mappedType == appView.dexItemFactory().objectType;
         return create(interfaceToClassChange.get().type, nullability, appView);
       } else {
         Builder builder = InterfaceCollection.builder();
@@ -206,38 +226,53 @@
               assert iface == rewritten || isKnown : "Rewritten implies program types thus known.";
               builder.addInterface(rewritten, isKnown);
             });
-        return create(mappedType, nullability, builder.build());
+        return create(mappedType, nullability, appView, builder.build());
       }
     }
-    return this;
+    return mappedType == type ? this : create(mappedType, nullability, appView, getInterfaces());
   }
 
   ClassTypeElement join(ClassTypeElement other, AppView<?> appView) {
-    Nullability nullability = nullability().join(other.nullability());
     if (!appView.enableWholeProgramOptimizations()) {
-      assert lazyInterfaces != null && lazyInterfaces.isEmpty();
-      assert other.lazyInterfaces != null && other.lazyInterfaces.isEmpty();
-      return ClassTypeElement.create(
+      assert lazyInterfaces != null;
+      assert lazyInterfaces.isEmpty();
+      assert other.lazyInterfaces != null;
+      assert other.lazyInterfaces.isEmpty();
+      return ClassTypeElement.createForD8(
           getClassType() == other.getClassType()
               ? getClassType()
               : appView.dexItemFactory().objectType,
-          nullability,
-          InterfaceCollection.empty());
+          nullability().join(other.nullability()));
     }
+    return joinWithClassHierarchy(other);
+  }
+
+  private ClassTypeElement joinWithClassHierarchy(ClassTypeElement other) {
+    assert appView != null;
+    assert appView.enableWholeProgramOptimizations();
     DexType lubType =
-        computeLeastUpperBoundOfClasses(
-            appView.appInfo().withClassHierarchy(), getClassType(), other.getClassType());
+        computeLeastUpperBoundOfClasses(appView.appInfo(), getClassType(), other.getClassType());
     InterfaceCollection c1lubItfs = getInterfaces();
     InterfaceCollection c2lubItfs = other.getInterfaces();
-    InterfaceCollection lubItfs = null;
-    if (c1lubItfs.equals(c2lubItfs)) {
-      lubItfs = c1lubItfs;
-    }
-    if (lubItfs == null) {
-      lubItfs =
-          computeLeastUpperBoundOfInterfaces(appView.withClassHierarchy(), c1lubItfs, c2lubItfs);
-    }
-    return ClassTypeElement.create(lubType, nullability, lubItfs);
+    InterfaceCollection lubItfs =
+        c1lubItfs.equals(c2lubItfs)
+            ? c1lubItfs
+            : computeLeastUpperBoundOfInterfaces(appView, c1lubItfs, c2lubItfs);
+    InterfaceCollection lubItfsDefault =
+        appView
+            .dexItemFactory()
+            .getOrComputeLeastUpperBoundOfImplementedInterfaces(lubType, appView);
+    Nullability lubNullability = nullability().join(other.nullability());
+
+    // If the computed interfaces are identical to the interfaces of `lubType`, then do not include
+    // the interfaces in the ClassTypeElement. This canonicalization of interfaces reduces memory,
+    // but also reduces the amount of work involved in lens rewriting class type elements (the null
+    // element does not require any rewriting).
+    //
+    // From a correctness point of view, both solutions should work.
+    return lubItfs.equals(lubItfsDefault)
+        ? create(lubType, lubNullability, appView)
+        : create(lubType, lubNullability, appView, lubItfs);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index ecc466a..25cb05d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.analysis.type;
 
+import static java.util.Collections.emptySet;
+
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -11,6 +13,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
 import java.util.function.Function;
 
 /** The base abstraction of lattice elements for local type analysis. */
@@ -68,14 +71,28 @@
     return ReferenceTypeElement.getNullType();
   }
 
-  public TypeElement fixupClassTypeReferences(
+  public final TypeElement fixupClassTypeReferences(
       AppView<? extends AppInfoWithClassHierarchy> appView, Function<DexType, DexType> mapping) {
+    return fixupClassTypeReferences(appView, mapping, emptySet());
+  }
+
+  public TypeElement fixupClassTypeReferences(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      Function<DexType, DexType> mapping,
+      Set<DexType> prunedTypes) {
     return this;
   }
 
   public final TypeElement rewrittenWithLens(
       AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens graphLens) {
-    return fixupClassTypeReferences(appView, graphLens::lookupType);
+    return rewrittenWithLens(appView, graphLens, emptySet());
+  }
+
+  public final TypeElement rewrittenWithLens(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      GraphLens graphLens,
+      Set<DexType> prunedTypes) {
+    return fixupClassTypeReferences(appView, graphLens::lookupType, prunedTypes);
   }
 
   public boolean isNullable() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 1ef9cf5..508b115 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -93,7 +93,8 @@
 
     assert verifyLambdaInterfaces(returnType, lambdaInterfaceSet, objectType);
 
-    return ClassTypeElement.create(objectType, Nullability.maybeNull(), lambdaInterfaceSet);
+    return ClassTypeElement.create(
+        objectType, Nullability.maybeNull(), appView.withClassHierarchy(), lambdaInterfaceSet);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
index b2f291a..b4141c6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -4,14 +4,18 @@
 
 package com.android.tools.r8.ir.optimize.info;
 
+import static java.util.Collections.emptySet;
+
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Set;
 
 /**
  * Optimization info for fields.
@@ -34,8 +38,15 @@
 
   public MutableFieldOptimizationInfo fixupClassTypeReferences(
       AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
+    return fixupClassTypeReferences(appView, lens, emptySet());
+  }
+
+  public MutableFieldOptimizationInfo fixupClassTypeReferences(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      GraphLens lens,
+      Set<DexType> prunedTypes) {
     if (dynamicUpperBoundType != null) {
-      dynamicUpperBoundType = dynamicUpperBoundType.rewrittenWithLens(appView, lens);
+      dynamicUpperBoundType = dynamicUpperBoundType.rewrittenWithLens(appView, lens, prunedTypes);
     }
     if (dynamicLowerBoundType != null) {
       TypeElement dynamicLowerBoundType =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
index 256f80a..b384693 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.ir.conversion.FieldOptimizationFeedback;
 import com.android.tools.r8.ir.conversion.MethodOptimizationFeedback;
 import com.android.tools.r8.shaking.AppInfoWithLivenessModifier;
@@ -28,8 +29,16 @@
   public void fixupOptimizationInfos(
       AppView<?> appView, ExecutorService executorService, OptimizationInfoFixer fixer)
       throws ExecutionException {
+    fixupOptimizationInfos(appView.appInfo().classes(), executorService, fixer);
+  }
+
+  public void fixupOptimizationInfos(
+      Iterable<DexProgramClass> classes,
+      ExecutorService executorService,
+      OptimizationInfoFixer fixer)
+      throws ExecutionException {
     ThreadUtils.processItems(
-        appView.appInfo().classes(),
+        classes,
         clazz -> {
           for (DexEncodedField field : clazz.fields()) {
             FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index e33f49c..03eee35 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.optimize.info;
 
+import static java.util.Collections.emptySet;
+
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
@@ -147,13 +149,20 @@
 
   public UpdatableMethodOptimizationInfo fixupClassTypeReferences(
       AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
+    return fixupClassTypeReferences(appView, lens, emptySet());
+  }
+
+  public UpdatableMethodOptimizationInfo fixupClassTypeReferences(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      GraphLens lens,
+      Set<DexType> prunedTypes) {
     if (returnsObjectWithUpperBoundType != null) {
       returnsObjectWithUpperBoundType =
-          returnsObjectWithUpperBoundType.rewrittenWithLens(appView, lens);
+          returnsObjectWithUpperBoundType.rewrittenWithLens(appView, lens, prunedTypes);
     }
     if (returnsObjectWithLowerBoundType != null) {
       TypeElement returnsObjectWithLowerBoundType =
-          this.returnsObjectWithLowerBoundType.rewrittenWithLens(appView, lens);
+          this.returnsObjectWithLowerBoundType.rewrittenWithLens(appView, lens, prunedTypes);
       if (returnsObjectWithLowerBoundType.isClassType()) {
         this.returnsObjectWithLowerBoundType = returnsObjectWithLowerBoundType.asClassType();
       } else {
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 8f606c1..577ecc2 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -18,6 +18,11 @@
 import com.android.tools.r8.graph.GenericSignatureTypeVariableRemover;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -30,6 +35,8 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 import java.util.function.Predicate;
 
 public class TreePruner {
@@ -63,15 +70,18 @@
             this::isAttributeReferencingPrunedItem);
   }
 
-  public DirectMappedDexApplication run() {
+  public DirectMappedDexApplication run(ExecutorService executorService) throws ExecutionException {
     DirectMappedDexApplication application = appView.appInfo().app().asDirect();
     Timing timing = application.timing;
     timing.begin("Pruning application...");
     try {
       DirectMappedDexApplication.Builder builder = removeUnused(application);
-      return prunedTypes.isEmpty() && !appView.options().configurationDebugging
-          ? application
-          : builder.build();
+      DirectMappedDexApplication newApplication =
+          prunedTypes.isEmpty() && !appView.options().configurationDebugging
+              ? application
+              : builder.build();
+      fixupOptimizationInfo(newApplication, executorService);
+      return newApplication;
     } finally {
       timing.end();
     }
@@ -99,7 +109,7 @@
             && !options.forceProguardCompatibility) {
           // The class is only needed as a type but never instantiated. Make it abstract to reflect
           // this.
-          if (clazz.accessFlags.isFinal()) {
+          if (clazz.isFinal()) {
             // We cannot mark this class abstract, as it is final (not supported on Android).
             // However, this might extend an abstract class and we might have removed the
             // corresponding methods in this class. This might happen if we only keep this
@@ -127,7 +137,7 @@
 
   private void pruneUnusedInterfaces(DexProgramClass clazz) {
     Set<DexType> reachableInterfaces = new LinkedHashSet<>();
-    for (DexType type : clazz.interfaces.values) {
+    for (DexType type : clazz.getInterfaces()) {
       retainReachableInterfacesFrom(type, reachableInterfaces);
     }
     if (!reachableInterfaces.isEmpty()) {
@@ -382,6 +392,33 @@
     return Collections.unmodifiableCollection(methodsToKeepForConfigurationDebugging);
   }
 
+  private void fixupOptimizationInfo(
+      DirectMappedDexApplication application, ExecutorService executorService)
+      throws ExecutionException {
+    // Clear the type elements cache due to redundant interface removal.
+    appView.dexItemFactory().clearTypeElementsCache();
+
+    OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
+    feedback.fixupOptimizationInfos(
+        application.classes(),
+        executorService,
+        new OptimizationInfoFixer() {
+          @Override
+          public void fixup(DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) {
+            optimizationInfo.fixupClassTypeReferences(appView, appView.graphLens(), prunedTypes);
+          }
+
+          @Override
+          public void fixup(
+              DexEncodedMethod method, UpdatableMethodOptimizationInfo optimizationInfo) {
+            optimizationInfo.fixupClassTypeReferences(appView, appView.graphLens(), prunedTypes);
+          }
+        });
+
+    // Verify that the fixup did not lead to the caching of any elements.
+    assert appView.dexItemFactory().verifyNoCachedTypeElements();
+  }
+
   private boolean verifyNoDeadFields(DexProgramClass clazz) {
     for (DexEncodedField field : clazz.fields()) {
       // Pinned field which type is never instantiated are always null, they are marked as dead
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
index c07c431..609c892 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
@@ -76,8 +76,7 @@
   public void testReferenceWidth() {
     DexItemFactory dexItemFactory = new DexItemFactory();
     ClassTypeElement referenceType =
-        ClassTypeElement.create(
-            dexItemFactory.objectType, Nullability.maybeNull(), InterfaceCollection.empty());
+        ClassTypeElement.createForD8(dexItemFactory.objectType, Nullability.maybeNull());
     assertFalse(referenceType.isSinglePrimitive());
     assertFalse(referenceType.isWidePrimitive());
     assertEquals(1, referenceType.requiredRegisters());