Refactor optimization info to have a base interface

Change-Id: I41cfb8f150e098454a0392276599f037cfb91b15
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index ada1c8a..1e10fa9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 public class DexEncodedField extends DexEncodedMember<DexEncodedField, DexField>
     implements StructuralItem<DexEncodedField> {
@@ -116,18 +117,15 @@
     return false;
   }
 
+  @Override
   public FieldOptimizationInfo getOptimizationInfo() {
     return optimizationInfo;
   }
 
   public synchronized MutableFieldOptimizationInfo getMutableOptimizationInfo() {
-    if (optimizationInfo.isDefaultFieldOptimizationInfo()) {
-      MutableFieldOptimizationInfo mutableOptimizationInfo = new MutableFieldOptimizationInfo();
-      optimizationInfo = mutableOptimizationInfo;
-      return mutableOptimizationInfo;
-    }
-    assert optimizationInfo.isMutableFieldOptimizationInfo();
-    return optimizationInfo.asMutableFieldOptimizationInfo();
+    MutableFieldOptimizationInfo mutableInfo = optimizationInfo.toMutableOptimizationInfo();
+    optimizationInfo = mutableInfo;
+    return mutableInfo;
   }
 
   public void setOptimizationInfo(MutableFieldOptimizationInfo info) {
@@ -197,6 +195,12 @@
     return asProgramField(definitions);
   }
 
+  @Override
+  public <T> T apply(
+      Function<DexEncodedField, T> fieldConsumer, Function<DexEncodedMethod, T> methodConsumer) {
+    return fieldConsumer.apply(this);
+  }
+
   public ProgramField asProgramField(DexDefinitionSupplier definitions) {
     assert getHolderType().isClassType();
     DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(getReference()));
@@ -376,9 +380,9 @@
       annotations = from.annotations();
       staticValue = from.staticValue;
       optimizationInfo =
-          from.optimizationInfo.isDefaultFieldOptimizationInfo()
-              ? DefaultFieldOptimizationInfo.getInstance()
-              : from.optimizationInfo.mutableCopy();
+          from.optimizationInfo.isMutableOptimizationInfo()
+              ? from.optimizationInfo.asMutableFieldOptimizationInfo().mutableCopy()
+              : from.optimizationInfo;
       deprecated = from.isDeprecated();
       d8R8Synthesized = from.isD8R8Synthesized();
     }
@@ -416,9 +420,7 @@
               staticValue,
               deprecated,
               d8R8Synthesized);
-      if (optimizationInfo.isMutableFieldOptimizationInfo()) {
-        dexEncodedField.setOptimizationInfo(optimizationInfo.asMutableFieldOptimizationInfo());
-      }
+      dexEncodedField.optimizationInfo = optimizationInfo;
       buildConsumer.accept(dexEncodedField);
       return dexEncodedField;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index e1bb25e..271fd68 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -3,7 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
 import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
+import java.util.function.Consumer;
+import java.util.function.Function;
 
 public abstract class DexEncodedMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
     extends DexDefinition {
@@ -63,6 +66,24 @@
 
   public abstract ProgramMember<D, R> asProgramMember(DexDefinitionSupplier definitions);
 
+  public abstract <T> T apply(
+      Function<DexEncodedField, T> fieldConsumer, Function<DexEncodedMethod, T> methodConsumer);
+
+  public void apply(
+      Consumer<DexEncodedField> fieldConsumer, Consumer<DexEncodedMethod> methodConsumer) {
+    apply(
+        field -> {
+          fieldConsumer.accept(field);
+          return null;
+        },
+        method -> {
+          methodConsumer.accept(method);
+          return null;
+        });
+  }
+
+  public abstract MemberOptimizationInfo<?> getOptimizationInfo();
+
   @Override
   public final boolean equals(Object other) {
     if (other == this) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 6bd54aa..449747b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -58,8 +58,8 @@
 import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationWithMinApiInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.MutableMethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
-import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.android.tools.r8.ir.synthetic.FieldAccessorBuilder;
@@ -93,6 +93,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.IntPredicate;
 import org.objectweb.asm.Opcodes;
 
@@ -436,6 +437,12 @@
     return asProgramMethod(definitions);
   }
 
+  @Override
+  public <T> T apply(
+      Function<DexEncodedField, T> fieldConsumer, Function<DexEncodedMethod, T> methodConsumer) {
+    return methodConsumer.apply(this);
+  }
+
   public DexClassAndMethod asDexClassAndMethod(DexDefinitionSupplier definitions) {
     assert getReference().holder.isClassType();
     DexClass clazz = definitions.definitionForHolder(getReference());
@@ -1445,20 +1452,20 @@
     return m1.getReference().compareTo(m2.getReference());
   }
 
+  @Override
   public MethodOptimizationInfo getOptimizationInfo() {
     checkIfObsolete();
     return optimizationInfo;
   }
 
-  public synchronized UpdatableMethodOptimizationInfo getMutableOptimizationInfo() {
+  public synchronized MutableMethodOptimizationInfo getMutableOptimizationInfo() {
     checkIfObsolete();
-    UpdatableMethodOptimizationInfo updatableMethodOptimizationInfo =
-        optimizationInfo.asUpdatableMethodOptimizationInfo();
-    this.optimizationInfo = updatableMethodOptimizationInfo;
-    return updatableMethodOptimizationInfo;
+    MutableMethodOptimizationInfo mutableInfo = optimizationInfo.toMutableOptimizationInfo();
+    optimizationInfo = mutableInfo;
+    return mutableInfo;
   }
 
-  public void setOptimizationInfo(UpdatableMethodOptimizationInfo info) {
+  public void setOptimizationInfo(MutableMethodOptimizationInfo info) {
     checkIfObsolete();
     optimizationInfo = info;
   }
@@ -1542,9 +1549,9 @@
       code = from.code;
       compilationState = CompilationState.NOT_PROCESSED;
       optimizationInfo =
-          from.optimizationInfo.isDefaultMethodOptimizationInfo()
-              ? DefaultMethodOptimizationInfo.getInstance()
-              : from.optimizationInfo.mutableCopy();
+          from.optimizationInfo.isMutableOptimizationInfo()
+              ? from.optimizationInfo.asMutableMethodOptimizationInfo().mutableCopy()
+              : from.optimizationInfo;
       kotlinMemberInfo = from.kotlinMemberInfo;
       classFileVersion = from.classFileVersion;
       this.d8R8Synthesized = d8R8Synthesized;
@@ -1679,8 +1686,9 @@
     }
 
     public Builder adjustOptimizationInfoAfterRemovingThisParameter() {
-      if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
-        optimizationInfo.asUpdatableMethodOptimizationInfo()
+      if (optimizationInfo.isMutableOptimizationInfo()) {
+        optimizationInfo
+            .asMutableMethodOptimizationInfo()
             .adjustOptimizationInfoAfterRemovingThisParameter();
       }
       return this;
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 8fd91e3..8ae52ef 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
@@ -899,11 +899,15 @@
   }
 
   private void computeReachabilitySensitivity(DexApplication application) {
-    application.classes().forEach(c -> {
-      if (c.hasReachabilitySensitiveAnnotation(options.itemFactory)) {
-        c.methods().forEach(m -> m.getMutableOptimizationInfo().setReachabilitySensitive(true));
-      }
-    });
+    application
+        .classes()
+        .forEach(
+            c -> {
+              if (c.hasReachabilitySensitiveAnnotation(options.itemFactory)) {
+                c.methods()
+                    .forEach(m -> m.getMutableOptimizationInfo().setReachabilitySensitive(true));
+              }
+            });
   }
 
   private void forEachSelectedOutliningMethod(
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 19bb6cc..0b47a53 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
@@ -85,9 +85,9 @@
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedInstanceFieldValueForEnumInstanceReason;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedLibraryInvokeReason;
 import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.MutableMethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
-import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -507,7 +507,7 @@
 
           @Override
           public void fixup(
-              DexEncodedMethod method, UpdatableMethodOptimizationInfo optimizationInfo) {
+              DexEncodedMethod method, MutableMethodOptimizationInfo optimizationInfo) {
             optimizationInfo
                 .fixupClassTypeReferences(appView, appView.graphLens())
                 .fixupAbstractReturnValue(appView, appView.graphLens())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
index 80fbd54..0408418 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
@@ -20,11 +20,6 @@
   }
 
   @Override
-  public MutableFieldOptimizationInfo mutableCopy() {
-    return new MutableFieldOptimizationInfo();
-  }
-
-  @Override
   public boolean cannotBeKept() {
     return false;
   }
@@ -60,12 +55,7 @@
   }
 
   @Override
-  public boolean isDefaultFieldOptimizationInfo() {
-    return true;
-  }
-
-  @Override
-  public DefaultFieldOptimizationInfo asDefaultFieldOptimizationInfo() {
-    return this;
+  public MutableFieldOptimizationInfo toMutableOptimizationInfo() {
+    return new MutableFieldOptimizationInfo();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 2b7ef1a..1df189a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -48,21 +48,6 @@
   }
 
   @Override
-  public boolean isDefaultMethodOptimizationInfo() {
-    return true;
-  }
-
-  @Override
-  public boolean isUpdatableMethodOptimizationInfo() {
-    return false;
-  }
-
-  @Override
-  public UpdatableMethodOptimizationInfo asUpdatableMethodOptimizationInfo() {
-    return mutableCopy();
-  }
-
-  @Override
   public boolean cannotBeKept() {
     return false;
   }
@@ -204,7 +189,7 @@
   }
 
   @Override
-  public UpdatableMethodOptimizationInfo mutableCopy() {
-    return new UpdatableMethodOptimizationInfo();
+  public MutableMethodOptimizationInfo toMutableOptimizationInfo() {
+    return new MutableMethodOptimizationInfo();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationWithMinApiInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationWithMinApiInfo.java
index fc61ba6..a5e21d9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationWithMinApiInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationWithMinApiInfo.java
@@ -26,8 +26,9 @@
   }
 
   @Override
-  public UpdatableMethodOptimizationInfo mutableCopy() {
-    UpdatableMethodOptimizationInfo updatableMethodOptimizationInfo = super.mutableCopy();
+  public MutableMethodOptimizationInfo toMutableOptimizationInfo() {
+    MutableMethodOptimizationInfo updatableMethodOptimizationInfo =
+        super.toMutableOptimizationInfo();
     // Use null to specify that the min api is set to minApi.
     updatableMethodOptimizationInfo.setApiReferenceLevel(null);
     return updatableMethodOptimizationInfo;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
index e70a91f..8a098ac 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
@@ -12,9 +12,8 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
-public abstract class FieldOptimizationInfo {
-
-  public abstract MutableFieldOptimizationInfo mutableCopy();
+public abstract class FieldOptimizationInfo
+    implements MemberOptimizationInfo<MutableFieldOptimizationInfo> {
 
   public abstract boolean cannotBeKept();
 
@@ -56,20 +55,4 @@
   public abstract boolean isDead();
 
   public abstract boolean valueHasBeenPropagated();
-
-  public boolean isDefaultFieldOptimizationInfo() {
-    return false;
-  }
-
-  public DefaultFieldOptimizationInfo asDefaultFieldOptimizationInfo() {
-    return null;
-  }
-
-  public boolean isMutableFieldOptimizationInfo() {
-    return false;
-  }
-
-  public MutableFieldOptimizationInfo asMutableFieldOptimizationInfo() {
-    return null;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MemberOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MemberOptimizationInfo.java
new file mode 100644
index 0000000..e9ce890
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MemberOptimizationInfo.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, 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.ir.optimize.info;
+
+public interface MemberOptimizationInfo<
+    T extends MemberOptimizationInfo<T> & MutableOptimizationInfo> {
+
+  default boolean isMutableOptimizationInfo() {
+    return false;
+  }
+
+  default MutableMethodOptimizationInfo asMutableMethodOptimizationInfo() {
+    return null;
+  }
+
+  default MutableFieldOptimizationInfo asMutableFieldOptimizationInfo() {
+    return null;
+  }
+
+  T toMutableOptimizationInfo();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 0190cd0..41c145f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -21,7 +21,8 @@
 import java.util.BitSet;
 import java.util.Set;
 
-public abstract class MethodOptimizationInfo {
+public abstract class MethodOptimizationInfo
+    implements MemberOptimizationInfo<MutableMethodOptimizationInfo> {
 
   enum InlinePreference {
     NeverInline,
@@ -29,12 +30,6 @@
     Default
   }
 
-  public abstract boolean isDefaultMethodOptimizationInfo();
-
-  public abstract boolean isUpdatableMethodOptimizationInfo();
-
-  public abstract UpdatableMethodOptimizationInfo asUpdatableMethodOptimizationInfo();
-
   public abstract boolean cannotBeKept();
 
   public abstract boolean classInitializerMayBePostponed();
@@ -104,8 +99,6 @@
 
   public abstract boolean hasApiReferenceLevel();
 
-  public abstract UpdatableMethodOptimizationInfo mutableCopy();
-
   public static OptionalBool isApiSafeForInlining(
       MethodOptimizationInfo caller, MethodOptimizationInfo inlinee, InternalOptions options) {
     if (!options.apiModelingOptions().enableApiCallerIdentification) {
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 b4141c6..9150ea0 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
@@ -24,7 +24,8 @@
  * updated directly, meaning that updates may become visible to concurrently processed methods in
  * the {@link com.android.tools.r8.ir.conversion.IRConverter}.
  */
-public class MutableFieldOptimizationInfo extends FieldOptimizationInfo {
+public class MutableFieldOptimizationInfo extends FieldOptimizationInfo
+    implements MutableOptimizationInfo {
 
   private static final int FLAGS_CANNOT_BE_KEPT = 1 << 0;
   private static final int FLAGS_IS_DEAD = 1 << 1;
@@ -62,7 +63,6 @@
     return this;
   }
 
-  @Override
   public MutableFieldOptimizationInfo mutableCopy() {
     MutableFieldOptimizationInfo copy = new MutableFieldOptimizationInfo();
     copy.flags = flags;
@@ -137,11 +137,16 @@
   }
 
   @Override
-  public boolean isMutableFieldOptimizationInfo() {
+  public boolean isMutableOptimizationInfo() {
     return true;
   }
 
   @Override
+  public MutableFieldOptimizationInfo toMutableOptimizationInfo() {
+    return this;
+  }
+
+  @Override
   public MutableFieldOptimizationInfo asMutableFieldOptimizationInfo() {
     return this;
   }
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/MutableMethodOptimizationInfo.java
similarity index 95%
rename from src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
rename to src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 85e2576..95d817f 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/MutableMethodOptimizationInfo.java
@@ -28,7 +28,8 @@
 import java.util.Optional;
 import java.util.Set;
 
-public class UpdatableMethodOptimizationInfo extends MethodOptimizationInfo {
+public class MutableMethodOptimizationInfo extends MethodOptimizationInfo
+    implements MutableOptimizationInfo {
 
   private Set<DexType> initializedClassesOnNormalExit =
       DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
@@ -130,13 +131,13 @@
 
   private int flags = DEFAULT_FLAGS;
 
-  UpdatableMethodOptimizationInfo() {
+  MutableMethodOptimizationInfo() {
     // Intentionally left empty, just use the default values.
   }
 
   // Copy constructor used to create a mutable copy. Do not forget to copy from template when a new
   // field is added.
-  private UpdatableMethodOptimizationInfo(UpdatableMethodOptimizationInfo template) {
+  private MutableMethodOptimizationInfo(MutableMethodOptimizationInfo template) {
     flags = template.flags;
     initializedClassesOnNormalExit = template.initializedClassesOnNormalExit;
     returnedArgument = template.returnedArgument;
@@ -153,12 +154,12 @@
     apiReferenceLevel = template.apiReferenceLevel;
   }
 
-  public UpdatableMethodOptimizationInfo fixupClassTypeReferences(
+  public MutableMethodOptimizationInfo fixupClassTypeReferences(
       AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
     return fixupClassTypeReferences(appView, lens, emptySet());
   }
 
-  public UpdatableMethodOptimizationInfo fixupClassTypeReferences(
+  public MutableMethodOptimizationInfo fixupClassTypeReferences(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       GraphLens lens,
       Set<DexType> prunedTypes) {
@@ -180,13 +181,13 @@
     return this;
   }
 
-  public UpdatableMethodOptimizationInfo fixupAbstractReturnValue(
+  public MutableMethodOptimizationInfo fixupAbstractReturnValue(
       AppView<AppInfoWithLiveness> appView, GraphLens lens) {
     abstractReturnValue = abstractReturnValue.rewrittenWithLens(appView, lens);
     return this;
   }
 
-  public UpdatableMethodOptimizationInfo fixupInstanceInitializerInfo(
+  public MutableMethodOptimizationInfo fixupInstanceInitializerInfo(
       AppView<AppInfoWithLiveness> appView, GraphLens lens) {
     instanceInitializerInfoCollection =
         instanceInitializerInfoCollection.rewrittenWithLens(appView, lens);
@@ -214,21 +215,6 @@
   }
 
   @Override
-  public boolean isDefaultMethodOptimizationInfo() {
-    return false;
-  }
-
-  @Override
-  public boolean isUpdatableMethodOptimizationInfo() {
-    return true;
-  }
-
-  @Override
-  public UpdatableMethodOptimizationInfo asUpdatableMethodOptimizationInfo() {
-    return this;
-  }
-
-  @Override
   public boolean cannotBeKept() {
     return isFlagSet(CANNOT_BE_KEPT_FLAG);
   }
@@ -511,14 +497,28 @@
     return apiReferenceLevel != null;
   }
 
-  public UpdatableMethodOptimizationInfo setApiReferenceLevel(AndroidApiLevel apiReferenceLevel) {
-    this.apiReferenceLevel = Optional.ofNullable(apiReferenceLevel);
+  @Override
+  public boolean isMutableOptimizationInfo() {
+    return true;
+  }
+
+  @Override
+  public MutableMethodOptimizationInfo toMutableOptimizationInfo() {
     return this;
   }
 
   @Override
-  public UpdatableMethodOptimizationInfo mutableCopy() {
-    return new UpdatableMethodOptimizationInfo(this);
+  public MutableMethodOptimizationInfo asMutableMethodOptimizationInfo() {
+    return this;
+  }
+
+  public MutableMethodOptimizationInfo setApiReferenceLevel(AndroidApiLevel apiReferenceLevel) {
+    this.apiReferenceLevel = Optional.ofNullable(apiReferenceLevel);
+    return this;
+  }
+
+  public MutableMethodOptimizationInfo mutableCopy() {
+    return new MutableMethodOptimizationInfo(this);
   }
 
   public void adjustOptimizationInfoAfterRemovingThisParameter() {
@@ -536,7 +536,8 @@
         || returnedArgument > 0;
     returnedArgument =
         returnedArgument == DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT
-            ? DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT : returnedArgument - 1;
+            ? DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT
+            : returnedArgument - 1;
     // mayHaveSideEffects: `this` Argument didn't have side effects, so removing it doesn't affect
     //   whether or not the method may have side effects.
     // returnValueOnlyDependsOnArguments:
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableOptimizationInfo.java
new file mode 100644
index 0000000..bf34966
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableOptimizationInfo.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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.ir.optimize.info;
+
+public interface MutableOptimizationInfo {}
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 b384693..26516df 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
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 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.DexProgramClass;
 import com.android.tools.r8.ir.conversion.FieldOptimizationFeedback;
@@ -23,7 +24,18 @@
 
     void fixup(DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo);
 
-    void fixup(DexEncodedMethod method, UpdatableMethodOptimizationInfo optimizationInfo);
+    void fixup(DexEncodedMethod method, MutableMethodOptimizationInfo optimizationInfo);
+
+    default void fixup(DexEncodedMember<?, ?> member) {
+      MemberOptimizationInfo<?> optimizationInfo = member.getOptimizationInfo();
+      if (optimizationInfo.isMutableOptimizationInfo()) {
+        member.apply(
+            field -> {
+              fixup(field, optimizationInfo.asMutableFieldOptimizationInfo());
+            },
+            method -> fixup(method, optimizationInfo.asMutableMethodOptimizationInfo()));
+      }
+    }
   }
 
   public void fixupOptimizationInfos(
@@ -38,26 +50,7 @@
       OptimizationInfoFixer fixer)
       throws ExecutionException {
     ThreadUtils.processItems(
-        classes,
-        clazz -> {
-          for (DexEncodedField field : clazz.fields()) {
-            FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
-            if (optimizationInfo.isMutableFieldOptimizationInfo()) {
-              fixer.fixup(field, optimizationInfo.asMutableFieldOptimizationInfo());
-            } else {
-              assert optimizationInfo.isDefaultFieldOptimizationInfo();
-            }
-          }
-          for (DexEncodedMethod method : clazz.methods()) {
-            MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
-            if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
-              fixer.fixup(method, optimizationInfo.asUpdatableMethodOptimizationInfo());
-            } else {
-              assert optimizationInfo.isDefaultMethodOptimizationInfo();
-            }
-          }
-        },
-        executorService);
+        classes, clazz -> clazz.members().forEach(fixer::fixup), executorService);
   }
 
   public void modifyAppInfoWithLiveness(Consumer<AppInfoWithLivenessModifier> consumer) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 5a12983..dd781e8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -36,7 +36,7 @@
       AppInfoWithLiveness.modifier();
   private final Map<DexEncodedField, MutableFieldOptimizationInfo> fieldOptimizationInfos =
       new IdentityHashMap<>();
-  private final Map<DexEncodedMethod, UpdatableMethodOptimizationInfo> methodOptimizationInfos =
+  private final Map<DexEncodedMethod, MutableMethodOptimizationInfo> methodOptimizationInfos =
       new IdentityHashMap<>();
   private final Map<DexEncodedMethod, ConstraintWithTarget> processed = new IdentityHashMap<>();
 
@@ -46,24 +46,23 @@
     if (info != null) {
       return info;
     }
-    info = field.getOptimizationInfo().mutableCopy();
+    info = field.getOptimizationInfo().toMutableOptimizationInfo().mutableCopy();
     fieldOptimizationInfos.put(field, info);
     return info;
   }
 
-  private synchronized UpdatableMethodOptimizationInfo getMethodOptimizationInfoForUpdating(
+  private synchronized MutableMethodOptimizationInfo getMethodOptimizationInfoForUpdating(
       DexEncodedMethod method) {
-    UpdatableMethodOptimizationInfo info = methodOptimizationInfos.get(method);
+    MutableMethodOptimizationInfo info = methodOptimizationInfos.get(method);
     if (info != null) {
       return info;
     }
-    info = method.getOptimizationInfo().mutableCopy();
+    info = method.getOptimizationInfo().toMutableOptimizationInfo().mutableCopy();
     methodOptimizationInfos.put(method, info);
     return info;
   }
 
-  private UpdatableMethodOptimizationInfo getMethodOptimizationInfoForUpdating(
-      ProgramMethod method) {
+  private MutableMethodOptimizationInfo getMethodOptimizationInfoForUpdating(ProgramMethod method) {
     return getMethodOptimizationInfoForUpdating(method.getDefinition());
   }
 
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 ef2f149..df989fe 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -18,10 +18,10 @@
 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.MutableMethodOptimizationInfo;
 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;
@@ -402,7 +402,7 @@
 
           @Override
           public void fixup(
-              DexEncodedMethod method, UpdatableMethodOptimizationInfo optimizationInfo) {
+              DexEncodedMethod method, MutableMethodOptimizationInfo optimizationInfo) {
             optimizationInfo.fixupClassTypeReferences(appView, appView.graphLens(), prunedTypes);
           }
         });