Revert "Introduce a structure of preservation requirements for program items."

This reverts commit 603f1327998f91889d06840bd386c7fdfa4e3c8b.

Reason for revert: breaks enum unboxing optimizations

Change-Id: I04d991962244610aac237d002378ab07b7470760
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 4dce9d4..ea55ec4 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableMap;
@@ -14,11 +13,9 @@
 import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.IdentityHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
@@ -266,14 +263,6 @@
     return true;
   }
 
-  public <T extends DexReference> boolean assertPinnedNotModified(KeepInfoCollection keepInfo) {
-    List<DexReference> pinnedItems = new ArrayList<>();
-    keepInfo.forEachPinnedType(pinnedItems::add);
-    keepInfo.forEachPinnedMethod(pinnedItems::add);
-    keepInfo.forEachPinnedField(pinnedItems::add);
-    return assertReferencesNotModified(pinnedItems);
-  }
-
   public <T extends DexReference> boolean assertReferencesNotModified(Iterable<T> references) {
     for (DexReference reference : references) {
       if (reference.isDexField()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 667c1ff..c64dff4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
@@ -25,7 +26,6 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.DefaultTreePrunerConfiguration;
 import com.android.tools.r8.shaking.Enqueuer;
-import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.shaking.TreePrunerConfiguration;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
@@ -190,7 +190,7 @@
           resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
       return field != null
           && isDeadProtoExtensionField(
-              field, appInfo.getFieldAccessInfoCollection(), appInfo.getKeepInfo());
+              field, appInfo.getFieldAccessInfoCollection(), appInfo.getPinnedItems());
     }
     return false;
   }
@@ -198,8 +198,8 @@
   public boolean isDeadProtoExtensionField(
       ProgramField field,
       FieldAccessInfoCollection<?> fieldAccessInfoCollection,
-      KeepInfoCollection keepInfo) {
-    if (keepInfo.getFieldInfo(field).isPinned()) {
+      Set<DexReference> pinnedItems) {
+    if (pinnedItems.contains(field.getReference())) {
       return false;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 483d2c1..3239c8c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.enums;
 
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 
 import com.android.tools.r8.graph.AppView;
@@ -326,17 +325,6 @@
       return;
     }
     ImmutableSet<DexType> enumsToUnbox = ImmutableSet.copyOf(this.enumsUnboxingCandidates.keySet());
-    // Update keep info on any of the enum methods of the removed classes.
-    appView
-        .appInfo()
-        .getKeepInfo()
-        .mutate(
-            keepInfo -> {
-              for (DexType type : enumsToUnbox) {
-                DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
-                clazz.forEachProgramMethod(keepInfo::unpinMethod);
-              }
-            });
     enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumsToUnbox);
     NestedGraphLense enumUnboxingLens = new TreeFixer(enumsToUnbox).fixupTypeReferences();
     appView.setUnboxedEnums(enumUnboxerRewriter.getEnumsToUnbox());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index 8c23763..b5530c2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -13,11 +13,11 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxer.Reason;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -146,17 +146,22 @@
     // A holder type, for field or method, should block enum unboxing only if the enum type is
     // also kept. This is to allow the keep rule -keepclassmembers to be used on enums while
     // enum unboxing can still be performed.
-    KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
-    keepInfo.forEachPinnedType(this::removePinnedCandidate);
-    keepInfo.forEachPinnedField((DexField field) -> removePinnedIfNotHolder(field, field.type));
-    keepInfo.forEachPinnedMethod(
-        (DexMethod method) -> {
-          DexProto proto = method.proto;
-          removePinnedIfNotHolder(method, proto.returnType);
-          for (DexType parameterType : proto.parameters.values) {
-            removePinnedIfNotHolder(method, parameterType);
-          }
-        });
+    for (DexReference item : appView.appInfo().getPinnedItems()) {
+      if (item.isDexType()) {
+        removePinnedCandidate(item.asDexType());
+      } else if (item.isDexField()) {
+        DexField field = item.asDexField();
+        removePinnedIfNotHolder(field, field.type);
+      } else {
+        assert item.isDexMethod();
+        DexMethod method = item.asDexMethod();
+        DexProto proto = method.proto;
+        removePinnedIfNotHolder(method, proto.returnType);
+        for (DexType parameterType : proto.parameters.values) {
+          removePinnedIfNotHolder(method, parameterType);
+        }
+      }
+    }
   }
 
   private void removePinnedIfNotHolder(DexMember<?, ?> member, DexType type) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index c65cefa..28928ef 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -28,11 +29,14 @@
   @Override
   public void done(Enqueuer enqueuer) {
     DexType kotlinMetadataType = appView.dexItemFactory().kotlinMetadataType;
+    DexClass kotlinMetadataClass =
+        appView.appInfo().definitionForWithoutExistenceAssert(kotlinMetadataType);
     // We will process kotlin.Metadata even if the type is not present in the program, as long as
     // the annotation will be in the output
     boolean keepMetadata =
-        // This is true for any non-program type (also missing) and for kept program types.
-        enqueuer.isPinned(kotlinMetadataType);
+        enqueuer.isPinned(kotlinMetadataType)
+            || enqueuer.isMissing(kotlinMetadataType)
+            || (kotlinMetadataClass != null && kotlinMetadataClass.isNotProgramClass());
     enqueuer.forAllLiveClasses(
         clazz -> {
           clazz.setKotlinInfo(
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
index 54decce..31e239b 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
@@ -254,9 +254,8 @@
 
     // Remove all of the bridges in the eligible subclasses.
     for (DexProgramClass subclass : eligibleSubclasses) {
-      assert !appView.appInfo().isPinned(method);
       DexEncodedMethod removed = subclass.removeMethod(method);
-      assert removed != null;
+      assert removed != null && !appView.appInfo().isPinned(removed.method);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 02aca6b..584b84b 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -305,15 +305,14 @@
               if (appView.appInfo().isPinned(ica.getInner())) {
                 return false;
               }
-              DexType outer = ica.getOuter();
-              if (outer != null && appView.appInfo().isPinned(outer)) {
+              if (appView.appInfo().isPinned(ica.getOuter())) {
                 return false;
               }
               if (finalKeepForThisInnerClass && ica.getInner() == clazz.type) {
                 return false;
               }
               if (finalKeepForThisEnclosingClass
-                  && outer == clazz.type
+                  && ica.getOuter() == clazz.type
                   && classesToRetainInnerClassAttributeFor.contains(ica.getInner())) {
                 return false;
               }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 6a2c794..989ae9a 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -136,14 +136,10 @@
    * will have been removed from the code.
    */
   public final Set<DexCallSite> callSites;
-  /** Collection of keep requirements for the program. */
-  private final KeepInfoCollection keepInfo;
-  /**
-   * Set of kept items that are allowed to be publicized.
-   *
-   * <p>TODO(b/156715504): merge into keep info.
-   */
-  private final Set<DexReference> allowAccessModification;
+  /** Set of all items that have to be kept independent of whether they are used. */
+  final Set<DexReference> pinnedItems;
+  /** Set of kept items that are allowed to be publicized. */
+  final Set<DexReference> allowAccessModification;
   /** All items with assumemayhavesideeffects rule. */
   public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects;
   /** All items with assumenosideeffects rule. */
@@ -221,7 +217,7 @@
       SortedMap<DexMethod, ProgramMethodSet> directInvokes,
       SortedMap<DexMethod, ProgramMethodSet> staticInvokes,
       Set<DexCallSite> callSites,
-      KeepInfoCollection keepInfo,
+      Set<DexReference> pinnedItems,
       Set<DexReference> allowAccessModification,
       Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
       Map<DexReference, ProguardMemberRule> noSideEffects,
@@ -257,7 +253,7 @@
     this.liveMethods = liveMethods;
     this.fieldAccessInfoCollection = fieldAccessInfoCollection;
     this.objectAllocationInfoCollection = objectAllocationInfoCollection;
-    this.keepInfo = keepInfo;
+    this.pinnedItems = pinnedItems;
     this.allowAccessModification = allowAccessModification;
     this.mayHaveSideEffects = mayHaveSideEffects;
     this.noSideEffects = noSideEffects;
@@ -308,7 +304,7 @@
       SortedMap<DexMethod, ProgramMethodSet> directInvokes,
       SortedMap<DexMethod, ProgramMethodSet> staticInvokes,
       Set<DexCallSite> callSites,
-      KeepInfoCollection keepInfo,
+      Set<DexReference> pinnedItems,
       Set<DexReference> allowAccessModification,
       Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
       Map<DexReference, ProguardMemberRule> noSideEffects,
@@ -344,7 +340,7 @@
     this.liveMethods = liveMethods;
     this.fieldAccessInfoCollection = fieldAccessInfoCollection;
     this.objectAllocationInfoCollection = objectAllocationInfoCollection;
-    this.keepInfo = keepInfo;
+    this.pinnedItems = pinnedItems;
     this.allowAccessModification = allowAccessModification;
     this.mayHaveSideEffects = mayHaveSideEffects;
     this.noSideEffects = noSideEffects;
@@ -396,7 +392,7 @@
         previous.directInvokes,
         previous.staticInvokes,
         previous.callSites,
-        previous.keepInfo,
+        previous.pinnedItems,
         previous.allowAccessModification,
         previous.mayHaveSideEffects,
         previous.noSideEffects,
@@ -447,7 +443,9 @@
         previous.directInvokes,
         previous.staticInvokes,
         previous.callSites,
-        extendPinnedItems(previous, additionalPinnedItems),
+        additionalPinnedItems == null
+            ? previous.pinnedItems
+            : CollectionUtils.mergeSets(previous.pinnedItems, additionalPinnedItems),
         previous.allowAccessModification,
         previous.mayHaveSideEffects,
         previous.noSideEffects,
@@ -473,44 +471,7 @@
         previous.constClassReferences,
         previous.initClassReferences);
     copyMetadataFromPrevious(previous);
-    assert keepInfo.verifyNoneArePinned(removedClasses, previous);
-  }
-
-  private static KeepInfoCollection extendPinnedItems(
-      AppInfoWithLiveness previous, Collection<DexReference> additionalPinnedItems) {
-    if (additionalPinnedItems == null || additionalPinnedItems.isEmpty()) {
-      return previous.keepInfo;
-    }
-    return previous.keepInfo.mutate(
-        collection -> {
-          for (DexReference reference : additionalPinnedItems) {
-            if (reference.isDexType()) {
-              DexProgramClass clazz =
-                  asProgramClassOrNull(previous.definitionFor(reference.asDexType()));
-              if (clazz != null) {
-                collection.pinClass(clazz);
-              }
-            } else if (reference.isDexMethod()) {
-              DexMethod method = reference.asDexMethod();
-              DexProgramClass clazz = asProgramClassOrNull(previous.definitionFor(method.holder));
-              if (clazz != null) {
-                DexEncodedMethod definition = clazz.lookupMethod(method);
-                if (definition != null) {
-                  collection.pinMethod(clazz, definition);
-                }
-              }
-            } else {
-              DexField field = reference.asDexField();
-              DexProgramClass clazz = asProgramClassOrNull(previous.definitionFor(field.holder));
-              if (clazz != null) {
-                DexEncodedField definition = clazz.lookupField(field);
-                if (definition != null) {
-                  collection.pinField(clazz, definition);
-                }
-              }
-            }
-          }
-        });
+    assert removedClasses == null || assertNoItemRemoved(previous.pinnedItems, removedClasses);
   }
 
   public AppInfoWithLiveness(
@@ -530,7 +491,7 @@
     this.liveMethods = previous.liveMethods;
     this.fieldAccessInfoCollection = previous.fieldAccessInfoCollection;
     this.objectAllocationInfoCollection = previous.objectAllocationInfoCollection;
-    this.keepInfo = previous.keepInfo;
+    this.pinnedItems = previous.pinnedItems;
     this.allowAccessModification = previous.allowAccessModification;
     this.mayHaveSideEffects = previous.mayHaveSideEffects;
     this.noSideEffects = previous.noSideEffects;
@@ -834,7 +795,7 @@
     if (info != null && info.isRead()) {
       return true;
     }
-    return keepInfo.isPinned(field, this)
+    return isPinned(field)
         // Fields in the class that is synthesized by D8/R8 would be used soon.
         || field.holder.isD8R8SynthesizedClassType()
         // For library classes we don't know whether a field is read.
@@ -906,7 +867,7 @@
     return method.getDefinition().hasCode()
         && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
         && !neverReprocess.contains(reference)
-        && !keepInfo.getMethodInfo(method).isPinned();
+        && !pinnedItems.contains(reference);
   }
 
   public boolean mayPropagateValueFor(DexReference reference) {
@@ -969,7 +930,7 @@
 
   public boolean isPinned(DexReference reference) {
     assert checkIfObsolete();
-    return keepInfo.isPinned(reference, this);
+    return pinnedItems.contains(reference);
   }
 
   public boolean hasPinnedInstanceInitializer(DexType type) {
@@ -985,8 +946,9 @@
     return false;
   }
 
-  public KeepInfoCollection getKeepInfo() {
-    return keepInfo;
+  public Set<DexReference> getPinnedItems() {
+    assert checkIfObsolete();
+    return pinnedItems;
   }
 
   /**
@@ -1060,7 +1022,7 @@
         // TODO(sgjesse): Rewrite call sites as well? Right now they are only used by minification
         //   after second tree shaking.
         callSites,
-        keepInfo.rewrite(lens),
+        lens.rewriteReferencesConservatively(pinnedItems),
         lens.rewriteReferencesConservatively(allowAccessModification),
         rewriteReferenceKeys(mayHaveSideEffects, lens::lookupReference),
         rewriteReferenceKeys(noSideEffects, lens::lookupReference),
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 01032ab..8d6df10 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -92,7 +92,6 @@
 import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
 import com.android.tools.r8.shaking.EnqueuerWorklist.EnqueuerAction;
 import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
-import com.android.tools.r8.shaking.KeepInfoCollection.MutableKeepInfoCollection;
 import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
@@ -306,8 +305,11 @@
    */
   private final Set<DexReference> reportedMissing = Sets.newIdentityHashSet();
 
-  /** Collection of keep requirements for the program. */
-  private final MutableKeepInfoCollection keepInfo = new MutableKeepInfoCollection();
+  /**
+   * A set of references that we are keeping due to keep rules. This may differ from the root set
+   * due to dependent keep rules.
+   */
+  private final Set<DexReference> pinnedItems = Sets.newIdentityHashSet();
 
   /**
    * A set of references that we are keeping due to keep rules, which we are allowed to publicize.
@@ -544,10 +546,6 @@
     return clazz;
   }
 
-  public boolean isPinned(DexType type) {
-    return keepInfo.isPinned(type, appInfo);
-  }
-
   private void addLiveNonProgramType(DexClass clazz) {
     assert clazz.isNotProgramClass();
     // Fast path to avoid the worklist when the class is already seen.
@@ -643,7 +641,6 @@
     if (item.isDexClass()) {
       DexProgramClass clazz = item.asDexClass().asProgramClass();
       KeepReasonWitness witness = graphReporter.reportKeepClass(precondition, rules, clazz);
-      keepInfo.pinClass(clazz);
       if (clazz.isAnnotation()) {
         workList.enqueueMarkAnnotationInstantiatedAction(clazz, witness);
       } else if (clazz.isInterface()) {
@@ -666,7 +663,6 @@
       DexEncodedField field = item.asDexEncodedField();
       DexProgramClass holder = getProgramClassOrNull(field.holder());
       if (holder != null) {
-        keepInfo.pinField(holder, field);
         workList.enqueueMarkFieldKeptAction(
             new ProgramField(holder, field),
             graphReporter.reportKeepField(precondition, rules, field));
@@ -675,7 +671,6 @@
       DexEncodedMethod encodedMethod = item.asDexEncodedMethod();
       DexProgramClass holder = getProgramClassOrNull(encodedMethod.holder());
       if (holder != null) {
-        keepInfo.pinMethod(holder, encodedMethod);
         workList.enqueueMarkMethodKeptAction(
             new ProgramMethod(holder, encodedMethod),
             graphReporter.reportKeepMethod(precondition, rules, encodedMethod));
@@ -683,7 +678,7 @@
     } else {
       throw new IllegalArgumentException(item.toString());
     }
-    setAllowAccessModification(item.toReference(), rules);
+    addPinnedItem(item.toReference(), rules);
   }
 
   private void enqueueFirstNonSerializableClassInitializer(
@@ -1319,7 +1314,7 @@
       boolean skipTracing =
           appView.withGeneratedExtensionRegistryShrinker(
               shrinker ->
-                  shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, keepInfo),
+                  shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, pinnedItems),
               false);
       if (skipTracing) {
         addDeadProtoTypeCandidate(field.getHolder());
@@ -1378,7 +1373,7 @@
       boolean skipTracing =
           appView.withGeneratedExtensionRegistryShrinker(
               shrinker ->
-                  shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, keepInfo),
+                  shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, pinnedItems),
               false);
       if (skipTracing) {
         addDeadProtoTypeCandidate(field.getHolder());
@@ -1813,14 +1808,12 @@
             holder,
             (dexType, ignored) -> {
               if (holder.isProgramClass()) {
-                DexProgramClass holderClass = holder.asProgramClass();
-                keepInfo.pinClass(holderClass);
-                setAllowAccessModification(holderClass.type);
-                rootSet.shouldNotBeMinified(holder.toReference());
+                DexReference holderReference = holder.toReference();
+                addPinnedItem(holderReference);
+                rootSet.shouldNotBeMinified(holderReference);
                 for (DexEncodedMember<?, ?> member : holder.members()) {
-                  keepInfo.pinMember(holderClass, member);
                   DexMember<?, ?> memberReference = member.toReference();
-                  setAllowAccessModification(memberReference);
+                  addPinnedItem(memberReference);
                   rootSet.shouldNotBeMinified(memberReference);
                 }
               }
@@ -2468,7 +2461,7 @@
             (type, subTypeConsumer, lambdaConsumer) ->
                 objectAllocationInfoCollection.forEachInstantiatedSubType(
                     type, subTypeConsumer, lambdaConsumer, appInfo),
-            reference -> keepInfo.isPinned(reference, appInfo))
+            pinnedItems::contains)
         .forEach(
             target ->
                 markVirtualDispatchTargetAsLive(
@@ -2535,8 +2528,7 @@
       // TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
       // marking for not renaming it is in the root set.
       workList.enqueueMarkMethodKeptAction(new ProgramMethod(clazz, valuesMethod), reason);
-      keepInfo.pinMethod(clazz, valuesMethod);
-      setAllowAccessModification(valuesMethod.toReference());
+      addPinnedItem(valuesMethod.toReference());
       rootSet.shouldNotBeMinified(valuesMethod.toReference());
     }
   }
@@ -2640,11 +2632,16 @@
     return appInfoWithLiveness;
   }
 
-  private void setAllowAccessModification(DexReference reference) {
-    allowAccessModification.put(reference, OptionalBool.unknown());
+  public boolean isPinned(DexReference reference) {
+    return pinnedItems.contains(reference);
   }
 
-  private void setAllowAccessModification(DexReference reference, Set<ProguardKeepRuleBase> rules) {
+  private boolean addPinnedItem(DexReference reference) {
+    allowAccessModification.put(reference, OptionalBool.unknown());
+    return pinnedItems.add(reference);
+  }
+
+  private boolean addPinnedItem(DexReference reference, Set<ProguardKeepRuleBase> rules) {
     assert rules != null;
     assert !rules.isEmpty();
     OptionalBool allowAccessModificationOfReference =
@@ -2661,6 +2658,11 @@
       }
       allowAccessModification.put(reference, allowAccessModificationOfReference);
     }
+    return pinnedItems.add(reference);
+  }
+
+  public boolean isMissing(DexType type) {
+    return missingTypes.contains(type);
   }
 
   private static class SyntheticAdditions {
@@ -2673,7 +2675,7 @@
     Map<DexType, DexClasspathClass> syntheticClasspathClasses = new IdentityHashMap<>();
 
     // Subset of live methods that need to be pinned.
-    Set<ProgramMethod> pinnedMethods = Sets.newIdentityHashSet();
+    Set<DexMethod> pinnedMethods = Sets.newIdentityHashSet();
 
     // Subset of synthesized classes that need to be added to the main-dex file.
     Set<DexType> mainDexTypes = Sets.newIdentityHashSet();
@@ -2706,7 +2708,7 @@
 
     void addLiveAndPinnedMethod(ProgramMethod method) {
       addLiveMethod(method);
-      pinnedMethods.add(method);
+      pinnedMethods.add(method.getDefinition().method);
     }
 
     void amendApplication(Builder appBuilder) {
@@ -2725,11 +2727,7 @@
       // All synthetic additions are initial tree shaking only. No need to track keep reasons.
       KeepReasonWitness fakeReason = enqueuer.graphReporter.fakeReportShouldNotBeUsed();
 
-      pinnedMethods.forEach(
-          method -> {
-            enqueuer.keepInfo.pinMethod(method);
-            enqueuer.setAllowAccessModification(method.getReference());
-          });
+      pinnedMethods.forEach(enqueuer::addPinnedItem);
       for (Pair<DexProgramClass, ProgramMethod> clazzAndContext :
           syntheticInstantiations.values()) {
         enqueuer.workList.enqueueMarkInstantiatedAction(
@@ -2920,7 +2918,7 @@
             toImmutableSortedMap(directInvokes, PresortedComparable::slowCompare),
             toImmutableSortedMap(staticInvokes, PresortedComparable::slowCompare),
             callSites,
-            keepInfo,
+            pinnedItems,
             allowAccessModification.keySet(),
             rootSet.mayHaveSideEffects,
             rootSet.noSideEffects,
@@ -3329,7 +3327,7 @@
     assert desugaredLambdaImplementationMethods.isEmpty()
         || options.desugarState == DesugarState.ON;
     for (DexMethod method : desugaredLambdaImplementationMethods) {
-      keepInfo.unpinMethod(method);
+      pinnedItems.remove(method);
       rootSet.prune(method);
     }
     desugaredLambdaImplementationMethods.clear();
@@ -3616,9 +3614,7 @@
         workList.enqueueMarkInstantiatedAction(
             clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method));
       }
-      if (!keepInfo.getFieldInfo(encodedField, clazz).isPinned()) {
-        keepInfo.pinField(clazz, encodedField);
-        setAllowAccessModification(encodedField.field);
+      if (addPinnedItem(encodedField.field)) {
         markFieldAsKept(new ProgramField(clazz, encodedField), KeepReason.reflectiveUseIn(method));
       }
     } else {
@@ -3805,16 +3801,14 @@
         // Add this interface to the set of pinned items to ensure that we do not merge the
         // interface into its unique subtype, if any.
         // TODO(b/145344105): This should be superseded by the unknown interface hierarchy.
-        keepInfo.pinClass(clazz);
-        setAllowAccessModification(clazz.type);
+        addPinnedItem(clazz.type);
         KeepReason reason = KeepReason.reflectiveUseIn(method);
         markInterfaceAsInstantiated(clazz, graphReporter.registerClass(clazz, reason));
 
         // Also pin all of its virtual methods to ensure that the devirtualizer does not perform
         // illegal rewritings of invoke-interface instructions into invoke-virtual instructions.
         for (DexEncodedMethod virtualMethod : clazz.virtualMethods()) {
-          keepInfo.pinMethod(clazz, virtualMethod);
-          setAllowAccessModification(virtualMethod.method);
+          addPinnedItem(virtualMethod.method);
           markVirtualMethodAsReachable(virtualMethod.method, true, null, reason);
         }
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
deleted file mode 100644
index a8cf2df..0000000
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.shaking;
-
-/** Immutable keep requirements for a class. */
-public final class KeepClassInfo extends KeepInfo {
-
-  // Requires all aspects of a class to be kept.
-  private static final KeepClassInfo TOP = new KeepClassInfo(true);
-
-  // Requires no aspects of a class to be kept.
-  private static final KeepClassInfo BOTTOM = new KeepClassInfo(false);
-
-  public static KeepClassInfo top() {
-    return TOP;
-  }
-
-  public static KeepClassInfo bottom() {
-    return BOTTOM;
-  }
-
-  private KeepClassInfo(boolean pinned) {
-    super(pinned);
-  }
-
-  public Builder builder() {
-    return new Builder(this);
-  }
-
-  public static class Builder extends KeepInfo.Builder<Builder, KeepClassInfo> {
-
-    private Builder(KeepClassInfo original) {
-      super(original);
-    }
-
-    @Override
-    public KeepClassInfo top() {
-      return TOP;
-    }
-
-    @Override
-    public KeepClassInfo bottom() {
-      return BOTTOM;
-    }
-
-    @Override
-    public Builder self() {
-      return this;
-    }
-
-    @Override
-    public boolean isEqualTo(KeepClassInfo other) {
-      return true;
-    }
-
-    @Override
-    public KeepClassInfo doBuild() {
-      return new KeepClassInfo(isPinned());
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
deleted file mode 100644
index 7416d8f..0000000
--- a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.shaking;
-
-/** Immutable keep requirements for a field. */
-public final class KeepFieldInfo extends KeepInfo {
-
-  // Requires all aspects of a field to be kept.
-  private static final KeepFieldInfo TOP = new KeepFieldInfo(true);
-
-  // Requires no aspects of a field to be kept.
-  private static final KeepFieldInfo BOTTOM = new KeepFieldInfo(false);
-
-  public static KeepFieldInfo top() {
-    return TOP;
-  }
-
-  public static KeepFieldInfo bottom() {
-    return BOTTOM;
-  }
-
-  private KeepFieldInfo(boolean pinned) {
-    super(pinned);
-  }
-
-  public Builder builder() {
-    return new Builder(this);
-  }
-
-  public static class Builder extends KeepInfo.Builder<Builder, KeepFieldInfo> {
-
-    private Builder(KeepFieldInfo original) {
-      super(original);
-    }
-
-    @Override
-    public KeepFieldInfo top() {
-      return TOP;
-    }
-
-    @Override
-    public KeepFieldInfo bottom() {
-      return BOTTOM;
-    }
-
-    @Override
-    public Builder self() {
-      return this;
-    }
-
-    @Override
-    public boolean isEqualTo(KeepFieldInfo other) {
-      return true;
-    }
-
-    @Override
-    public KeepFieldInfo doBuild() {
-      return new KeepFieldInfo(isPinned());
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
deleted file mode 100644
index 246eeb7..0000000
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.shaking;
-
-/** Keep information that can be associated with any item, i.e., class, method or field. */
-public abstract class KeepInfo {
-
-  private final boolean pinned;
-
-  public KeepInfo(boolean pinned) {
-    this.pinned = pinned;
-  }
-
-  public boolean isPinned() {
-    return pinned;
-  }
-
-  public abstract static class Builder<B extends Builder, K extends KeepInfo> {
-
-    public abstract B self();
-
-    public abstract K doBuild();
-
-    public abstract K top();
-
-    public abstract K bottom();
-
-    public abstract boolean isEqualTo(K other);
-
-    private K original;
-    private boolean pinned;
-
-    public Builder(K original) {
-      this.original = original;
-      pinned = original.isPinned();
-    }
-
-    public K build() {
-      if (internalIsEqualTo(original)) {
-        return original;
-      }
-      if (internalIsEqualTo(top())) {
-        return top();
-      }
-      if (internalIsEqualTo(bottom())) {
-        return bottom();
-      }
-      return doBuild();
-    }
-
-    private boolean internalIsEqualTo(K other) {
-      return isPinned() == other.isPinned() && isEqualTo(other);
-    }
-
-    public boolean isPinned() {
-      return pinned;
-    }
-
-    public B setPinned(boolean pinned) {
-      this.pinned = pinned;
-      return self();
-    }
-
-    public B pin() {
-      return setPinned(true);
-    }
-
-    public B unpin() {
-      return setPinned(false);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
deleted file mode 100644
index 1665e21..0000000
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ /dev/null
@@ -1,298 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.shaking;
-
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.DexDefinitionSupplier;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMember;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.graph.ProgramMethod;
-import java.util.Collection;
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-
-// Non-mutable collection of keep information pertaining to a program.
-public abstract class KeepInfoCollection {
-
-  /**
-   * Base accessor for keep info on a class.
-   *
-   * <p>Access may never be granted directly on DexType as the "keep info" for any non-program type
-   * is not the same as the default keep info for a program type. By typing the interface at program
-   * item we can eliminate errors where a reference to a non-program item results in optimizations
-   * assuming aspects of it can be changed when in fact they can not.
-   */
-  public abstract KeepClassInfo getClassInfo(DexProgramClass clazz);
-
-  /**
-   * Base accessor for keep info on a method.
-   *
-   * <p>See comment on class access for why this is typed at program method.
-   */
-  public abstract KeepMethodInfo getMethodInfo(DexEncodedMethod method, DexProgramClass holder);
-
-  /**
-   * Base accessor for keep info on a field.
-   *
-   * <p>See comment on class access for why this is typed at program field.
-   */
-  public abstract KeepFieldInfo getFieldInfo(DexEncodedField field, DexProgramClass holder);
-
-  public final KeepClassInfo getClassInfo(DexType type, DexDefinitionSupplier definitions) {
-    DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(type));
-    return clazz == null ? KeepClassInfo.top() : getClassInfo(clazz);
-  }
-
-  public final KeepMethodInfo getMethodInfo(ProgramMethod method) {
-    return getMethodInfo(method.getDefinition(), method.getHolder());
-  }
-
-  public final KeepMethodInfo getMethodInfo(DexMethod method, DexDefinitionSupplier definitions) {
-    DexProgramClass holder = asProgramClassOrNull(definitions.definitionFor(method.holder));
-    if (holder == null) {
-      return KeepMethodInfo.top();
-    }
-    DexEncodedMethod definition = holder.lookupMethod(method);
-    return definition == null ? KeepMethodInfo.bottom() : getMethodInfo(definition, holder);
-  }
-
-  public final KeepFieldInfo getFieldInfo(ProgramField field) {
-    return getFieldInfo(field.getDefinition(), field.getHolder());
-  }
-
-  public final KeepFieldInfo getFieldInfo(DexField field, DexDefinitionSupplier definitions) {
-    DexProgramClass holder = asProgramClassOrNull(definitions.definitionFor(field.holder));
-    if (holder == null) {
-      return KeepFieldInfo.top();
-    }
-    DexEncodedField definition = holder.lookupField(field);
-    return definition == null ? KeepFieldInfo.bottom() : getFieldInfo(definition, holder);
-  }
-
-  public final KeepInfo getInfo(DexReference reference, DexDefinitionSupplier definitions) {
-    if (reference.isDexType()) {
-      return getClassInfo(reference.asDexType(), definitions);
-    }
-    if (reference.isDexMethod()) {
-      return getMethodInfo(reference.asDexMethod(), definitions);
-    }
-    if (reference.isDexField()) {
-      return getFieldInfo(reference.asDexField(), definitions);
-    }
-    throw new Unreachable();
-  }
-
-  public final boolean isPinned(DexReference reference, DexDefinitionSupplier definitions) {
-    return getInfo(reference, definitions).isPinned();
-  }
-
-  public final boolean isPinned(DexType type, DexDefinitionSupplier definitions) {
-    return getClassInfo(type, definitions).isPinned();
-  }
-
-  public final boolean isPinned(DexMethod method, DexDefinitionSupplier definitions) {
-    return getMethodInfo(method, definitions).isPinned();
-  }
-
-  public final boolean isPinned(DexField field, DexDefinitionSupplier definitions) {
-    return getFieldInfo(field, definitions).isPinned();
-  }
-
-  public final boolean verifyNoneArePinned(Collection<DexType> types, AppInfo appInfo) {
-    for (DexType type : types) {
-      DexProgramClass clazz =
-          asProgramClassOrNull(appInfo.definitionForWithoutExistenceAssert(type));
-      assert clazz == null || !getClassInfo(clazz).isPinned();
-    }
-    return true;
-  }
-
-  // TODO(b/156715504): We should try to avoid the need for iterating pinned items.
-  @Deprecated
-  public abstract void forEachPinnedType(Consumer<DexType> consumer);
-
-  // TODO(b/156715504): We should try to avoid the need for iterating pinned items.
-  @Deprecated
-  public abstract void forEachPinnedMethod(Consumer<DexMethod> consumer);
-
-  // TODO(b/156715504): We should try to avoid the need for iterating pinned items.
-  @Deprecated
-  public abstract void forEachPinnedField(Consumer<DexField> consumer);
-
-  public abstract KeepInfoCollection rewrite(NestedGraphLense lens);
-
-  public abstract KeepInfoCollection mutate(Consumer<MutableKeepInfoCollection> mutator);
-
-  // Mutation interface for building up the keep info.
-  public static class MutableKeepInfoCollection extends KeepInfoCollection {
-
-    // These are typed at signatures but the interface should make sure never to allow access
-    // directly with a signature. See the comment in KeepInfoCollection.
-    private final Map<DexType, KeepClassInfo> keepClassInfo;
-    private final Map<DexMethod, KeepMethodInfo> keepMethodInfo;
-    private final Map<DexField, KeepFieldInfo> keepFieldInfo;
-
-    MutableKeepInfoCollection() {
-      this(new IdentityHashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>());
-    }
-
-    private MutableKeepInfoCollection(
-        Map<DexType, KeepClassInfo> keepClassInfo,
-        Map<DexMethod, KeepMethodInfo> keepMethodInfo,
-        Map<DexField, KeepFieldInfo> keepFieldInfo) {
-      this.keepClassInfo = keepClassInfo;
-      this.keepMethodInfo = keepMethodInfo;
-      this.keepFieldInfo = keepFieldInfo;
-    }
-
-    @Override
-    public KeepInfoCollection rewrite(NestedGraphLense lens) {
-      Map<DexType, KeepClassInfo> newClassInfo = new IdentityHashMap<>(keepClassInfo.size());
-      keepClassInfo.forEach(
-          (type, info) -> {
-            DexType newType = lens.lookupType(type);
-            assert !info.isPinned() || type == newType;
-            newClassInfo.put(newType, info);
-          });
-      Map<DexMethod, KeepMethodInfo> newMethodInfo = new IdentityHashMap<>(keepMethodInfo.size());
-      keepMethodInfo.forEach(
-          (method, info) -> {
-            Set<DexMethod> methods = lens.lookupMethodInAllContexts(method);
-            assert !info.isPinned() || methods.contains(method);
-            for (DexMethod newMethod : methods) {
-              // TODO(b/157012327): Should this distribute keep-info from pinned methods too?
-              if (!info.isPinned() || method == newMethod) {
-                newMethodInfo.put(newMethod, info);
-              }
-            }
-          });
-      Map<DexField, KeepFieldInfo> newFieldInfo = new IdentityHashMap<>(keepFieldInfo.size());
-      keepFieldInfo.forEach(
-          (field, info) -> {
-            DexField newField = lens.lookupField(field);
-            assert !info.isPinned() || field == newField;
-            newFieldInfo.put(newField, info);
-          });
-      return new MutableKeepInfoCollection(newClassInfo, newMethodInfo, newFieldInfo);
-    }
-
-    @Override
-    public KeepClassInfo getClassInfo(DexProgramClass clazz) {
-      return keepClassInfo.getOrDefault(clazz.type, KeepClassInfo.bottom());
-    }
-
-    @Override
-    public KeepMethodInfo getMethodInfo(DexEncodedMethod method, DexProgramClass holder) {
-      assert method.holder() == holder.type;
-      return keepMethodInfo.getOrDefault(method.method, KeepMethodInfo.bottom());
-    }
-
-    @Override
-    public KeepFieldInfo getFieldInfo(DexEncodedField field, DexProgramClass holder) {
-      assert field.holder() == holder.type;
-      return keepFieldInfo.getOrDefault(field.field, KeepFieldInfo.bottom());
-    }
-
-    public void pinClass(DexProgramClass clazz) {
-      KeepClassInfo info = getClassInfo(clazz);
-      if (!info.isPinned()) {
-        keepClassInfo.put(clazz.type, info.builder().pin().build());
-      }
-    }
-
-    public void pinMember(DexProgramClass holder, DexEncodedMember<?, ?> member) {
-      if (member.isDexEncodedMethod()) {
-        pinMethod(holder, member.asDexEncodedMethod());
-      } else {
-        assert member.isDexEncodedField();
-        pinField(holder, member.asDexEncodedField());
-      }
-    }
-
-    public void pinMethod(ProgramMethod programMethod) {
-      pinMethod(programMethod.getHolder(), programMethod.getDefinition());
-    }
-
-    public void pinMethod(DexProgramClass holder, DexEncodedMethod method) {
-      KeepMethodInfo info = getMethodInfo(method, holder);
-      if (!info.isPinned()) {
-        keepMethodInfo.put(method.method, info.builder().pin().build());
-      }
-    }
-
-    public void unpinMethod(ProgramMethod method) {
-      assert !getClassInfo(method.getHolder()).isPinned();
-      unpinMethod(method.getReference());
-    }
-
-    // TODO(b/156715504): We should never need to unpin items. Rather avoid pinning to begin with.
-    @Deprecated
-    public void unpinMethod(DexMethod method) {
-      KeepMethodInfo info = keepMethodInfo.get(method);
-      if (info != null && info.isPinned()) {
-        keepMethodInfo.put(method, info.builder().unpin().build());
-      }
-    }
-
-    public void pinField(ProgramField programField) {
-      pinField(programField.getHolder(), programField.getDefinition());
-    }
-
-    public void pinField(DexProgramClass holder, DexEncodedField field) {
-      KeepFieldInfo info = getFieldInfo(field, holder);
-      if (!info.isPinned()) {
-        keepFieldInfo.put(field.field, info.builder().pin().build());
-      }
-    }
-
-    @Override
-    public KeepInfoCollection mutate(Consumer<MutableKeepInfoCollection> mutator) {
-      mutator.accept(this);
-      return this;
-    }
-
-    @Override
-    public void forEachPinnedType(Consumer<DexType> consumer) {
-      keepClassInfo.forEach(
-          (type, info) -> {
-            if (info.isPinned()) {
-              consumer.accept(type);
-            }
-          });
-    }
-
-    @Override
-    public void forEachPinnedMethod(Consumer<DexMethod> consumer) {
-      keepMethodInfo.forEach(
-          (method, info) -> {
-            if (info.isPinned()) {
-              consumer.accept(method);
-            }
-          });
-    }
-
-    @Override
-    public void forEachPinnedField(Consumer<DexField> consumer) {
-      keepFieldInfo.forEach(
-          (field, info) -> {
-            if (info.isPinned()) {
-              consumer.accept(field);
-            }
-          });
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
deleted file mode 100644
index f3bb073..0000000
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.shaking;
-
-/** Immutable keep requirements for a method. */
-public final class KeepMethodInfo extends KeepInfo {
-
-  // Requires all aspects of a method to be kept.
-  private static final KeepMethodInfo TOP = new KeepMethodInfo(true);
-
-  // Requires no aspects of a method to be kept.
-  private static final KeepMethodInfo BOTTOM = new KeepMethodInfo(false);
-
-  public static KeepMethodInfo top() {
-    return TOP;
-  }
-
-  public static KeepMethodInfo bottom() {
-    return BOTTOM;
-  }
-
-  private KeepMethodInfo(boolean pinned) {
-    super(pinned);
-  }
-
-  public Builder builder() {
-    return new Builder(this);
-  }
-
-  public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
-
-    private Builder(KeepMethodInfo original) {
-      super(original);
-    }
-
-    @Override
-    public Builder self() {
-      return this;
-    }
-
-    @Override
-    public KeepMethodInfo top() {
-      return TOP;
-    }
-
-    @Override
-    public KeepMethodInfo bottom() {
-      return BOTTOM;
-    }
-
-    @Override
-    public boolean isEqualTo(KeepMethodInfo other) {
-      return true;
-    }
-
-    @Override
-    public KeepMethodInfo doBuild() {
-      return new KeepMethodInfo(isPinned());
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
index c6a1725..0979ef9 100644
--- a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
+++ b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
@@ -49,10 +50,8 @@
         getClassesWithLibraryMethodOverrides(appView);
 
     // Remove all types that are pinned from the initial set of non-escaping classes.
-    appView
-        .appInfo()
-        .getKeepInfo()
-        .forEachPinnedType(initialNonEscapingClassesWithLibraryMethodOverrides::remove);
+    DexReference.filterDexType(appView.appInfo().pinnedItems.stream())
+        .forEach(initialNonEscapingClassesWithLibraryMethodOverrides::remove);
 
     return initialNonEscapingClassesWithLibraryMethodOverrides;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 859e313..ffb940a 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -689,59 +689,53 @@
   // TODO(b/67934426): Test this code.
   public static void writeSeeds(
       AppInfoWithLiveness appInfo, PrintStream out, Predicate<DexType> include) {
-    appInfo
-        .getKeepInfo()
-        .forEachPinnedType(
-            type -> {
-              if (include.test(type)) {
-                out.println(type.toSourceString());
-              }
-            });
-    appInfo
-        .getKeepInfo()
-        .forEachPinnedField(
-            field -> {
-              if (include.test(field.holder)) {
-                out.println(
-                    field.holder.toSourceString()
-                        + ": "
-                        + field.type.toSourceString()
-                        + " "
-                        + field.name.toSourceString());
-              }
-            });
-    appInfo
-        .getKeepInfo()
-        .forEachPinnedMethod(
-            method -> {
-              if (!include.test(method.holder)) {
-                return;
-              }
-              out.print(method.holder.toSourceString() + ": ");
-              DexEncodedMethod encodedMethod = appInfo.definitionFor(method);
-              if (encodedMethod.accessFlags.isConstructor()) {
-                if (encodedMethod.accessFlags.isStatic()) {
-                  out.print(Constants.CLASS_INITIALIZER_NAME);
-                } else {
-                  String holderName = method.holder.toSourceString();
-                  String constrName = holderName.substring(holderName.lastIndexOf('.') + 1);
-                  out.print(constrName);
-                }
-              } else {
-                out.print(
-                    method.proto.returnType.toSourceString() + " " + method.name.toSourceString());
-              }
-              boolean first = true;
-              out.print("(");
-              for (DexType param : method.proto.parameters.values) {
-                if (!first) {
-                  out.print(",");
-                }
-                first = false;
-                out.print(param.toSourceString());
-              }
-              out.println(")");
-            });
+    for (DexReference seed : appInfo.getPinnedItems()) {
+      if (seed.isDexType()) {
+        if (include.test(seed.asDexType())) {
+          out.println(seed.toSourceString());
+        }
+      } else if (seed.isDexField()) {
+        DexField field = seed.asDexField();
+        if (include.test(field.holder)) {
+          out.println(
+              field.holder.toSourceString()
+                  + ": "
+                  + field.type.toSourceString()
+                  + " "
+                  + field.name.toSourceString());
+        }
+      } else {
+        assert seed.isDexMethod();
+        DexMethod method = seed.asDexMethod();
+        if (!include.test(method.holder)) {
+          continue;
+        }
+        out.print(method.holder.toSourceString() + ": ");
+        DexEncodedMethod encodedMethod = appInfo.definitionFor(method);
+        if (encodedMethod.accessFlags.isConstructor()) {
+          if (encodedMethod.accessFlags.isStatic()) {
+            out.print(Constants.CLASS_INITIALIZER_NAME);
+          } else {
+            String holderName = method.holder.toSourceString();
+            String constrName = holderName.substring(holderName.lastIndexOf('.') + 1);
+            out.print(constrName);
+          }
+        } else {
+          out.print(
+              method.proto.returnType.toSourceString() + " " + method.name.toSourceString());
+        }
+        boolean first = true;
+        out.print("(");
+        for (DexType param : method.proto.parameters.values) {
+          if (!first) {
+            out.print(",");
+          }
+          first = false;
+          out.print(param.toSourceString());
+        }
+        out.println(")");
+      }
+    }
     out.close();
   }
 
@@ -1649,11 +1643,14 @@
     }
 
     public boolean verifyKeptItemsAreKept(DexApplication application, AppInfo appInfo) {
+      Set<DexReference> pinnedItems =
+          appInfo.hasLiveness() ? appInfo.withLiveness().pinnedItems : null;
+
       // Create a mapping from each required type to the set of required members on that type.
       Map<DexType, Set<DexReference>> requiredReferencesPerType = new IdentityHashMap<>();
       for (DexReference reference : noShrinking.keySet()) {
         // Check that `pinnedItems` is a super set of the root set.
-        assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(reference)
+        assert pinnedItems == null || pinnedItems.contains(reference)
             : "Expected reference `" + reference.toSourceString() + "` to be pinned";
         if (reference.isDexType()) {
           DexType type = reference.asDexType();
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 3dd0f4f..da9038d 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -273,12 +273,7 @@
     // For all pinned fields, also pin the type of the field (because changing the type of the field
     // implicitly changes the signature of the field). Similarly, for all pinned methods, also pin
     // the return type and the parameter types of the method.
-    // TODO(b/156715504): Compute referenced-by-pinned in the keep info objects.
-    List<DexReference> pinnedItems = new ArrayList<>();
-    appInfo.getKeepInfo().forEachPinnedType(pinnedItems::add);
-    appInfo.getKeepInfo().forEachPinnedMethod(pinnedItems::add);
-    appInfo.getKeepInfo().forEachPinnedField(pinnedItems::add);
-    extractPinnedItems(pinnedItems, AbortReason.PINNED_SOURCE);
+    extractPinnedItems(appInfo.pinnedItems, AbortReason.PINNED_SOURCE);
 
     // TODO(christofferqa): Remove the invariant that the graph lense should not modify any
     // methods from the sets alwaysInline and noSideEffects (see use of assertNotModified).
@@ -685,7 +680,7 @@
     // that `invoke-super A.method` instructions, which are in one of the methods from C, needs to
     // be rewritten to `invoke-direct C.method$B`. This is valid even though A.method() is actually
     // pinned, because this rewriting does not affect A.method() in any way.
-    assert graphLense.assertPinnedNotModified(appInfo.getKeepInfo());
+    assert graphLense.assertReferencesNotModified(appInfo.pinnedItems);
 
     for (DexProgramClass clazz : appInfo.classes()) {
       for (DexEncodedMethod encodedMethod : clazz.methods()) {
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
index 512a9ff..105bf6b 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -103,7 +103,8 @@
         */
         "getstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
         "invokevirtual java/io/PrintStream/print(Ljava/lang/Object;)V",
-        "return");
+        "return"
+    );
 
     final String mainClassName = mainClass.name;
     String proguardConfig = StringUtils.lines(