Prune single caller inlined methods on-the-fly

Fixes: 130721661
Fixes: 202419103
Change-Id: I1a83ada3ee6cfdc6aed60126c9ac6f056aea08dd
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9c6d6c3..8617b7e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -52,6 +52,7 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
+import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.NestReducer;
 import com.android.tools.r8.ir.optimize.SwitchMapCollector;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxingCfMethods;
@@ -621,8 +622,7 @@
 
             new BridgeHoisting(appViewWithLiveness).run();
 
-            // TODO(b/130721661): Enable this assert.
-            // assert Inliner.verifyNoMethodsInlinedDueToSingleCallSite(appView);
+            assert Inliner.verifyAllSingleCallerMethodsHaveBeenPruned(appView);
 
             assert appView.allMergedClasses().verifyAllSourcesPruned(appViewWithLiveness);
             assert appView.validateUnboxedEnumsHaveBeenPruned();
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 450bc51..209e51c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -733,6 +733,11 @@
     setCode(builder.build(), appView);
   }
 
+  public void unsetCode() {
+    checkIfObsolete();
+    code = null;
+  }
+
   public boolean keepLocals(InternalOptions options) {
     if (options.testing.noLocalsTableOnInput) {
       return false;
@@ -908,24 +913,6 @@
     return getReference().toSourceString();
   }
 
-  public DexEncodedMethod toAbstractMethod() {
-    checkIfObsolete();
-    // 'final' wants this to be *not* overridden, while 'abstract' wants this to be implemented in
-    // a subtype, i.e., self contradict.
-    assert !accessFlags.isFinal();
-    // static abstract is an invalid access combination and we should never create that.
-    assert !accessFlags.isStatic();
-    return builder(this)
-        .modifyAccessFlags(MethodAccessFlags::setAbstract)
-        .setIsLibraryMethodOverrideIf(
-            isNonPrivateVirtualMethod() && !isLibraryMethodOverride().isUnknown(),
-            isLibraryMethodOverride())
-        .unsetCode()
-        .addBuildConsumer(
-            method -> OptimizationFeedbackSimple.getInstance().unsetBridgeInfo(method))
-        .build();
-  }
-
   /**
    * Generates a {@link DexCode} object for the given instructions.
    */
@@ -1369,6 +1356,10 @@
     return apiLevelForCode;
   }
 
+  public void clearApiLevelForCode(AppView<?> appView) {
+    this.apiLevelForCode = AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView);
+  }
+
   public void setApiLevelForCode(AndroidApiLevel apiLevel) {
     assert apiLevel != null;
     this.apiLevelForCode = apiLevel;
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index 23bd9f7..cd5e8de 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -127,6 +127,10 @@
     set(Constants.ACC_SYNCHRONIZED);
   }
 
+  public void demoteFromSynchronized() {
+    demote(Constants.ACC_SYNCHRONIZED);
+  }
+
   public void unsetSynchronized() {
     unset(Constants.ACC_SYNCHRONIZED);
   }
@@ -179,6 +183,14 @@
     set(Constants.ACC_ABSTRACT);
   }
 
+  public void demoteFromAbstract() {
+    demote(Constants.ACC_ABSTRACT);
+  }
+
+  public void promoteToAbstract() {
+    promote(Constants.ACC_ABSTRACT);
+  }
+
   public void unsetAbstract() {
     unset(Constants.ACC_ABSTRACT);
   }
@@ -191,6 +203,10 @@
     set(Constants.ACC_STRICT);
   }
 
+  public void demoteFromStrict() {
+    demote(Constants.ACC_STRICT);
+  }
+
   public void unsetStrict() {
     unset(Constants.ACC_STRICT);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index c03a7b8..84ec3c5 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -3,16 +3,21 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
+
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.kotlin.KotlinMethodLevelInfo;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
 
 /** Type representing a method definition in the programs compilation unit and its holder. */
 public final class ProgramMethod extends DexClassAndMethod
@@ -66,6 +71,44 @@
     definition.parameterAnnotationsList.collectIndexedItems(indexedItems);
   }
 
+  public void convertToAbstractOrThrowNullMethod(AppView<AppInfoWithLiveness> appView) {
+    if (!convertToAbstractMethodIfPossible(appView)) {
+      convertToThrowNullMethod(appView);
+    }
+  }
+
+  private boolean convertToAbstractMethodIfPossible(AppView<AppInfoWithLiveness> appView) {
+    boolean canBeAbstract =
+        (appView.options().canUseAbstractMethodOnNonAbstractClass()
+                || getHolder().isAbstract()
+                || getHolder().isInterface())
+            && !getAccessFlags().isNative()
+            && !getAccessFlags().isPrivate()
+            && !getAccessFlags().isStatic()
+            && !appView.appInfo().isFailedResolutionTarget(getReference());
+    if (canBeAbstract) {
+      MethodAccessFlags accessFlags = getAccessFlags();
+      accessFlags.demoteFromFinal();
+      accessFlags.demoteFromStrict();
+      accessFlags.demoteFromSynchronized();
+      accessFlags.promoteToAbstract();
+      getDefinition().clearApiLevelForCode(appView);
+      getDefinition().unsetCode();
+      getSimpleFeedback().unsetOptimizationInfoForAbstractMethod(this);
+    }
+    return canBeAbstract;
+  }
+
+  public void convertToThrowNullMethod(AppView<?> appView) {
+    MethodAccessFlags accessFlags = getAccessFlags();
+    accessFlags.demoteFromAbstract();
+    Code emptyThrowingCode = getDefinition().buildEmptyThrowingCode(appView.options());
+    getDefinition().setApiLevelForCode(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView));
+    getDefinition().setCode(emptyThrowingCode, appView);
+    getSimpleFeedback().markProcessed(getDefinition(), ConstraintWithTarget.ALWAYS);
+    getSimpleFeedback().unsetOptimizationInfoForThrowNullMethod(this);
+  }
+
   public void registerCodeReferences(UseRegistry<?> registry) {
     Code code = getDefinition().getCode();
     if (code != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java
index 92a127d..4899a50 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java
@@ -82,6 +82,12 @@
           continue;
         }
 
+        if (method.getDefinition().isDefaultInitializer()
+            && appView.hasProguardCompatibilityActions()
+            && appView.getProguardCompatibilityActions().isCompatInstantiated(method.getHolder())) {
+          continue;
+        }
+
         int numberOfCallSites = node.getNumberOfCallSites();
         if (numberOfCallSites == 1) {
           singleCallSite.add(reference);
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 1d1a698..4fde21a 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
@@ -270,7 +270,7 @@
               : null;
       this.enumUnboxer = EnumUnboxer.create(appViewWithLiveness);
       this.lensCodeRewriter = new LensCodeRewriter(appViewWithLiveness, enumUnboxer);
-      this.inliner = new Inliner(appViewWithLiveness, lensCodeRewriter);
+      this.inliner = new Inliner(appViewWithLiveness, this, lensCodeRewriter);
       this.outliner = Outliner.create(appViewWithLiveness);
       this.memberValuePropagation =
           options.enableValuePropagation ? new MemberValuePropagation(appViewWithLiveness) : null;
@@ -360,8 +360,7 @@
   public void convert(AppView<AppInfo> appView, ExecutorService executor)
       throws ExecutionException {
     LambdaDeserializationMethodRemover.run(appView);
-    workaroundAbstractMethodOnNonAbstractClassVerificationBug(
-        executor, OptimizationFeedbackIgnore.getInstance());
+    workaroundAbstractMethodOnNonAbstractClassVerificationBug(executor);
     DexApplication application = appView.appInfo().app();
     D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executor);
     InterfaceProcessor interfaceProcessor =
@@ -603,7 +602,7 @@
   }
 
   private void workaroundAbstractMethodOnNonAbstractClassVerificationBug(
-      ExecutorService executorService, OptimizationFeedback feedback) throws ExecutionException {
+      ExecutorService executorService) throws ExecutionException {
     if (!options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug()) {
       return;
     }
@@ -612,13 +611,8 @@
         appView.appInfo().classes(),
         clazz -> {
           if (!clazz.isAbstract()) {
-            clazz.forEachMethod(
-                method -> {
-                  if (method.isAbstract()) {
-                    method.accessFlags.unsetAbstract();
-                    finalizeEmptyThrowingCode(method, feedback);
-                  }
-                });
+            clazz.forEachProgramMethodMatching(
+                DexEncodedMethod::isAbstract, method -> method.convertToThrowNullMethod(appView));
           }
         },
         executorService);
@@ -633,8 +627,7 @@
     DexApplication application = appView.appInfo().app();
 
     computeReachabilitySensitivity(application);
-    workaroundAbstractMethodOnNonAbstractClassVerificationBug(
-        executorService, simpleOptimizationFeedback);
+    workaroundAbstractMethodOnNonAbstractClassVerificationBug(executorService);
 
     // The process is in two phases in general.
     // 1) Subject all DexEncodedMethods to optimization, except some optimizations that require
@@ -975,16 +968,6 @@
     processMethodsConcurrently(methods, executorService);
   }
 
-  public void optimizeSynthesizedClasses(
-      Collection<DexProgramClass> classes, ExecutorService executorService)
-      throws ExecutionException {
-    SortedProgramMethodSet methods = SortedProgramMethodSet.create();
-    for (DexProgramClass clazz : classes) {
-      clazz.forEachProgramMethod(methods::add);
-    }
-    processMethodsConcurrently(methods, executorService);
-  }
-
   public void optimizeSynthesizedMethod(ProgramMethod synthesizedMethod) {
     if (!synthesizedMethod.getDefinition().isProcessed()) {
       // Process the generated method, but don't apply any outlining.
@@ -1188,8 +1171,8 @@
             + ExceptionUtils.getMainStackTrace();
     assert !method.isProcessed()
             || !appView.enableWholeProgramOptimizations()
-            || !appView.appInfo().withLiveness().isNeverReprocessMethod(method.getReference())
-        : "Illegal reprocessing due to -neverreprocess rule: " + context.toSourceString();
+            || !appView.appInfo().withLiveness().isNeverReprocessMethod(context)
+        : "Unexpected reprocessing of method: " + context.toSourceString();
 
     if (typeChecker != null && !typeChecker.check(code)) {
       assert appView.enableWholeProgramOptimizations();
@@ -1200,14 +1183,14 @@
                   + method.toSourceString()
                   + "` does not type check and will be assumed to be unreachable.");
       options.reporter.warning(warning);
-      finalizeEmptyThrowingCode(method, feedback);
+      context.convertToThrowNullMethod(appView);
       return timing;
     }
 
     // This is the first point in time where we can assert that the types are sound. If this
     // assert fails, then the types that we have inferred are unsound, or the method does not type
     // check. In the latter case, the type checker should be extended to detect the issue such that
-    // we will return with finalizeEmptyThrowingCode() above.
+    // we will return with a throw-null method above.
     assert code.verifyTypes(appView);
     assert code.isConsistentSSA();
 
@@ -1653,13 +1636,6 @@
     }
   }
 
-  private void finalizeEmptyThrowingCode(DexEncodedMethod method, OptimizationFeedback feedback) {
-    assert options.isGeneratingClassFiles() || options.isGeneratingDex();
-    Code emptyThrowingCode = method.buildEmptyThrowingCode(options);
-    method.setCode(emptyThrowingCode, appView);
-    feedback.markProcessed(method, ConstraintWithTarget.ALWAYS);
-  }
-
   private void finalizeToCf(
       IRCode code, OptimizationFeedback feedback, MethodConversionOptions conversionOptions) {
     DexEncodedMethod method = code.method();
@@ -1984,6 +1960,7 @@
   public void pruneMethod(ProgramMethod method) {
     assert appView.enableWholeProgramOptimizations();
     assert method.getHolder().lookupMethod(method.getReference()) == null;
+    appView.withArgumentPropagator(argumentPropagator -> argumentPropagator.pruneMethod(method));
     if (inliner != null) {
       inliner.pruneMethod(method);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index a54b717..6435f37 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -82,10 +82,6 @@
       methodsToRevisit.forEach(this::add);
     }
 
-    public void put(PostOptimization postOptimization) {
-      put(postOptimization.methodsToRevisit());
-    }
-
     // Some optimizations may change methods, creating new instances of the encoded methods with a
     // new signature. The compiler needs to update the set of methods that must be reprocessed
     // according to the graph lens.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostOptimization.java b/src/main/java/com/android/tools/r8/ir/conversion/PostOptimization.java
deleted file mode 100644
index 30c873d..0000000
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostOptimization.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2019, 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.conversion;
-
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
-
-/**
- * An abstraction of optimizations that require post processing of methods.
- */
-public interface PostOptimization {
-
-  /** @return a set of methods that need post processing. */
-  ProgramMethodSet methodsToRevisit();
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index abf7239..32df085 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.optimize;
 
 import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
 import static com.google.common.base.Predicates.not;
 
 import com.android.tools.r8.androidapi.AvailableApiExceptions;
@@ -47,6 +48,7 @@
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Throw;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.LensCodeRewriter;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.conversion.PostMethodProcessor;
@@ -78,10 +80,12 @@
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 public class Inliner {
 
   protected final AppView<AppInfoWithLiveness> appView;
+  private final IRConverter converter;
   private final Set<DexMethod> extraNeverInlineMethods;
   private final LensCodeRewriter lensCodeRewriter;
   final MainDexInfo mainDexInfo;
@@ -96,13 +100,18 @@
   private final Map<DexEncodedMethod, ProgramMethod> doubleInlineeCandidates =
       new IdentityHashMap<>();
 
+  private final Map<DexProgramClass, ProgramMethodSet> singleCallerInlinedMethods =
+      new ConcurrentHashMap<>();
+
   private final AvailableApiExceptions availableApiExceptions;
 
   public Inliner(
       AppView<AppInfoWithLiveness> appView,
+      IRConverter converter,
       LensCodeRewriter lensCodeRewriter) {
     Kotlin.Intrinsics intrinsics = appView.dexItemFactory().kotlin.intrinsics;
     this.appView = appView;
+    this.converter = converter;
     this.extraNeverInlineMethods =
         appView.options().kotlinOptimizationOptions().disableKotlinSpecificOptimizations
             ? ImmutableSet.of()
@@ -1130,7 +1139,15 @@
               appView, code, inlinee.code, blockIterator, blocksToRemove, downcastClass);
 
           if (inlinee.reason == Reason.SINGLE_CALLER) {
+            assert converter.isInWave();
             feedback.markInlinedIntoSingleCallSite(singleTargetMethod);
+            if (singleCallerInlinedMethods.isEmpty()) {
+              converter.addWaveDoneAction(this::onWaveDone);
+            }
+            singleCallerInlinedMethods
+                .computeIfAbsent(
+                    singleTarget.getHolder(), ignoreKey(ProgramMethodSet::createConcurrent))
+                .add(singleTarget);
           }
 
           classInitializationAnalysis.notifyCodeHasChanged();
@@ -1304,10 +1321,37 @@
     singleInlineCallers.remove(method.getReference(), appView.graphLens());
   }
 
-  public static boolean verifyNoMethodsInlinedDueToSingleCallSite(AppView<?> appView) {
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
+  private void onWaveDone() {
+    singleCallerInlinedMethods.forEach(
+        (clazz, singleCallerInlinedMethodsForClass) -> {
+          // Convert and remove virtual single caller inlined methods to abstract or throw null.
+          singleCallerInlinedMethodsForClass.removeIf(
+              singleCallerInlinedMethod -> {
+                if (singleCallerInlinedMethod.getDefinition().belongsToVirtualPool() || true) {
+                  singleCallerInlinedMethod.convertToAbstractOrThrowNullMethod(appView);
+                  return true;
+                }
+                return false;
+              });
+
+          // Remove direct single caller inlined methods from the application.
+          if (!singleCallerInlinedMethodsForClass.isEmpty()) {
+            clazz
+                .getMethodCollection()
+                .removeMethods(
+                    singleCallerInlinedMethodsForClass.toDefinitionSet(
+                        SetUtils::newIdentityHashSet));
+            singleCallerInlinedMethodsForClass.forEach(converter::pruneMethod);
+          }
+        });
+    singleCallerInlinedMethods.clear();
+  }
+
+  public static boolean verifyAllSingleCallerMethodsHaveBeenPruned(AppView<?> appView) {
+    for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
       for (DexEncodedMethod method : clazz.methods()) {
-        assert !method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite();
+        assert !method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite() || !method.hasCode()
+            : "Method was single caller inlined: " + method.toSourceString();
       }
     }
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 26430a4..854a4e0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -638,9 +638,21 @@
     postMethodProcessorBuilder
         .getMethodsToReprocessBuilder()
         .rewrittenWithLens(appView)
-        .merge(dependencies)
-        .merge(methodsDependingOnLibraryModelisation)
-        .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods());
+        .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods())
+        .merge(
+            dependencies
+                .rewrittenWithLens(appView)
+                .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods())
+                .removeIf(
+                    appView,
+                    method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite()))
+        .merge(
+            methodsDependingOnLibraryModelisation
+                .rewrittenWithLens(appView)
+                .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods())
+                .removeIf(
+                    appView,
+                    method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite()));
     methodsDependingOnLibraryModelisation.clear();
 
     updateOptimizationInfos(executorService, feedback, treeFixerResult);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
index 16e4dab..78dc248 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
@@ -97,12 +97,15 @@
               DexProgramClass.asProgramClassOrNull(
                   appView.definitionFor(rewrittenReference.getHolderType()));
           ProgramMethod method = rewrittenReference.lookupOnProgramClass(holder);
-          if (method != null) {
-            for (Outline outline : outlinesForMethod) {
-              methodsPerOutline.computeIfAbsent(outline, ignoreKey(ArrayList::new)).add(method);
-            }
-          } else {
+          if (method == null) {
             assert false;
+            return;
+          }
+          if (method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite()) {
+            return;
+          }
+          for (Outline outline : outlinesForMethod) {
+            methodsPerOutline.computeIfAbsent(outline, ignoreKey(ArrayList::new)).add(method);
           }
         });
     return methodsPerOutline;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 2fbaa44..c73132f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -234,7 +234,13 @@
         LongLivedProgramMethodSetBuilder<?> referencedFromBuilder =
             classStaticizer.referencedFrom.remove(info);
         assert referencedFromBuilder != null;
-        referencedFrom = referencedFromBuilder.build(appView);
+        referencedFrom =
+            referencedFromBuilder
+                .rewrittenWithLens(appView)
+                .removeIf(
+                    appView,
+                    method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite())
+                .build(appView);
         materializedReferencedFromCollections.put(info, referencedFrom);
       } else {
         referencedFrom = ProgramMethodSet.empty();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index 080e691..922e198 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -213,4 +213,20 @@
     reprocessingCriteriaCollection = null;
     timing.end();
   }
+
+  /**
+   * Called by {@link IRConverter} at the end of a wave if a method is pruned by an optimization.
+   *
+   * <p>We only prune (1) direct single caller methods and (2) isX()/asX() virtual method overrides.
+   * For (2), we always transfer the argument information for the isX()/asX() method to its parent
+   * method using {@link #transferArgumentInformation(ProgramMethod, ProgramMethod)}, which unsets
+   * the argument information for the override.
+   *
+   * <p>Therefore, we assert that we only find a method state for direct methods.
+   */
+  public void pruneMethod(ProgramMethod method) {
+    assert codeScanner != null;
+    MethodState methodState = codeScanner.getMethodStates().removeOrElse(method, null);
+    assert methodState == null || method.getDefinition().belongsToDirectPool();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index c01508c..efa1985 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -81,7 +81,7 @@
             CallSiteOptimizationInfo callSiteOptimizationInfo =
                 method.getDefinition().getCallSiteOptimizationInfo();
             if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()
-                && !appView.appInfo().isNeverReprocessMethod(method.getReference())) {
+                && !appView.appInfo().isNeverReprocessMethod(method)) {
               methodsToReprocessBuilder.add(method, currentGraphLens);
               appView.testing().callSiteOptimizationInfoInspector.accept(method);
             }
@@ -107,11 +107,14 @@
                   method -> {
                     if (graphLens.internalGetNextMethodSignature(method.getReference())
                         != method.getReference()) {
-                      methodsToReprocessInClass.add(method);
+                      if (!method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite()) {
+                        methodsToReprocessInClass.add(method);
+                      }
                     } else {
                       AffectedMethodUseRegistry registry =
                           new AffectedMethodUseRegistry(appView, method, graphLens);
                       if (method.registerCodeReferencesWithResult(registry)) {
+                        assert !method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite();
                         methodsToReprocessInClass.add(method);
                       }
                     }
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 91289b7..45630fa 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -657,8 +657,9 @@
     return keepUnusedArguments.contains(method);
   }
 
-  public boolean isNeverReprocessMethod(DexMethod method) {
-    return neverReprocess.contains(method);
+  public boolean isNeverReprocessMethod(ProgramMethod method) {
+    return neverReprocess.contains(method.getReference())
+        || method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite();
   }
 
   public Set<DexMethod> getReprocessMethods() {
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 43d2d16..f837095 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -17,13 +17,13 @@
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.graph.ProgramMethod;
 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.logging.Log;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IterableUtils;
@@ -283,11 +283,13 @@
     return -1;
   }
 
-  private DexEncodedMethod[] reachableMethods(Iterable<DexEncodedMethod> methods, DexClass clazz) {
+  private DexEncodedMethod[] reachableMethods(
+      Iterable<DexEncodedMethod> methods, DexProgramClass clazz) {
     return reachableMethods(IterableUtils.ensureUnmodifiableList(methods), clazz);
   }
 
-  private DexEncodedMethod[] reachableMethods(List<DexEncodedMethod> methods, DexClass clazz) {
+  private DexEncodedMethod[] reachableMethods(
+      List<DexEncodedMethod> methods, DexProgramClass clazz) {
     AppInfoWithLiveness appInfo = appView.appInfo();
     InternalOptions options = appView.options();
     int firstUnreachable =
@@ -320,27 +322,11 @@
         if (Log.ENABLED) {
           Log.debug(getClass(), "Making method %s abstract.", method.getReference());
         }
-        // Final classes cannot be abstract, so we have to keep the method in that case.
-        // Also some other kinds of methods cannot be abstract, so keep them around.
-        boolean allowAbstract =
-            (options.canUseAbstractMethodOnNonAbstractClass() || clazz.isAbstract())
-                && !method.isFinal()
-                && !method.accessFlags.isNative()
-                && !method.accessFlags.isStrict()
-                && !method.isSynchronized()
-                && !method.accessFlags.isPrivate()
-                && !method.isStatic()
-                && !appInfo.isFailedResolutionTarget(method.getReference());
         // Private methods and static methods can only be targeted yet non-live as the result of
         // an invalid invoke. They will not actually be called at runtime but we have to keep them
         // as non-abstract (see above) to produce the same failure mode.
-        if (!allowAbstract) {
-          // If the method was not marked as live and we cannot make it abstract, set the api level
-          // to be min or unknown.
-          method.setApiLevelForCode(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView));
-        }
-        reachableMethods.add(
-            allowAbstract ? method.toAbstractMethod() : method.toEmptyThrowingMethod(options));
+        new ProgramMethod(clazz, method).convertToAbstractOrThrowNullMethod(appView);
+        reachableMethods.add(method);
       } else {
         if (Log.ENABLED) {
           Log.debug(getClass(), "Removing method %s.", method.getReference());
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
index 5fe5cf1..00749bb 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -7,11 +7,13 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
-import com.google.common.collect.Sets;
+import com.android.tools.r8.utils.SetUtils;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.IntFunction;
+import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
@@ -78,6 +80,10 @@
     return remove(method.getReference());
   }
 
+  public boolean removeIf(Predicate<? super T> predicate) {
+    return backing.values().removeIf(predicate);
+  }
+
   public int size() {
     return backing.size();
   }
@@ -88,7 +94,11 @@
 
   public Set<DexEncodedMethod> toDefinitionSet() {
     assert backing instanceof IdentityHashMap;
-    Set<DexEncodedMethod> definitions = Sets.newIdentityHashSet();
+    return toDefinitionSet(SetUtils::newIdentityHashSet);
+  }
+
+  public Set<DexEncodedMethod> toDefinitionSet(IntFunction<Set<DexEncodedMethod>> factory) {
+    Set<DexEncodedMethod> definitions = factory.apply(size());
     forEach(method -> definitions.add(method.getDefinition()));
     return definitions;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
index 911290b..ce4399b 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
@@ -68,7 +68,7 @@
 
   public void addAll(Iterable<ProgramMethod> methodsToAdd, GraphLens currentGraphLens) {
     assert verifyIsRewrittenWithLens(currentGraphLens);
-    methodsToAdd.forEach(method -> methods.add(method.getReference()));
+    methodsToAdd.forEach(method -> add(method, currentGraphLens));
   }
 
   public void clear() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/LibraryOverrideInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/LibraryOverrideInliningTest.java
index 0b58101..3882a7b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/LibraryOverrideInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/LibraryOverrideInliningTest.java
@@ -6,16 +6,14 @@
 
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -24,17 +22,14 @@
 @RunWith(Parameterized.class)
 public class LibraryOverrideInliningTest extends TestBase {
 
-  private final boolean disableInliningOfLibraryMethodOverrides;
   private final TestParameters parameters;
 
-  @Parameters(name = "{1}, disableInliningOfLibraryMethodOverrides: {0}")
-  public static List<Object[]> data() {
-    return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  public LibraryOverrideInliningTest(
-      boolean disableInliningOfLibraryMethodOverrides, TestParameters parameters) {
-    this.disableInliningOfLibraryMethodOverrides = disableInliningOfLibraryMethodOverrides;
+  public LibraryOverrideInliningTest(TestParameters parameters) {
     this.parameters = parameters;
   }
 
@@ -43,12 +38,8 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(LibraryOverrideInliningTest.class)
         .addKeepMainRule(TestClass.class)
-        .addOptionsModification(
-            options ->
-                options.disableInliningOfLibraryMethodOverrides =
-                    disableInliningOfLibraryMethodOverrides)
         .enableNeverClassInliningAnnotations()
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
             inspector -> {
@@ -64,12 +55,7 @@
 
               MethodSubject mainMethodSubject = testClassSubject.mainMethod();
               assertThat(mainMethodSubject, isPresent());
-
-              if (disableInliningOfLibraryMethodOverrides) {
-                assertThat(mainMethodSubject, invokesMethod(toStringMethodSubject));
-              } else {
-                assertThat(mainMethodSubject, not(invokesMethod(toStringMethodSubject)));
-              }
+              assertThat(mainMethodSubject, invokesMethod(toStringMethodSubject));
             })
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutputLines("Hello world!");
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 5bae6d3..105d097 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -6,8 +6,10 @@
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -120,7 +122,7 @@
                 assertThat(
                     inspector.clazz(
                         "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2"),
-                    isPresent());
+                    isAbsent());
               } else {
                 assertThat(
                     inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"),
@@ -164,7 +166,7 @@
                       !hasKotlinCGeneratedLambdaClasses
                           ? "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2"
                           : "class_inliner_lambda_j_style.MainKt$testStateful$1"),
-                  isPresent());
+                  onlyIf(hasKotlinCGeneratedLambdaClasses, isPresent()));
             });
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 0f534e0..282d8d7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -10,7 +10,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestParameters;
@@ -91,6 +90,11 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, testedClass.getOuterClassName());
               String propertyName = "primitiveProp";
@@ -124,6 +128,11 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, testedClass.getOuterClassName());
               String propertyName = "privateProp";
@@ -159,6 +168,11 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, testedClass.getOuterClassName());
               String propertyName = "internalProp";
@@ -193,6 +207,11 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, testedClass.getOuterClassName());
               String propertyName = "publicProp";
@@ -227,6 +246,11 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, testedClass.getOuterClassName());
               String propertyName = "privateLateInitProp";
@@ -260,6 +284,11 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass)
         .inspect(
             inspector -> {
+              if (true) {
+                checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, testedClass.getOuterClassName());
               String propertyName = "internalLateInitProp";
@@ -293,6 +322,11 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass)
         .inspect(
             inspector -> {
+              if (true) {
+                checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, testedClass.getOuterClassName());
               String propertyName = "publicLateInitProp";
@@ -375,9 +409,7 @@
     runTest("accessors", mainClass)
         .inspect(
             inspector -> {
-              if (allowAccessModification
-                  && (testParameters.isCfRuntime()
-                      || !kotlinParameters.is(KOTLINC_1_5_0, KotlinTargetVersion.JAVA_8))) {
+              if (allowAccessModification) {
                 checkClassIsRemoved(inspector, testedClass.getClassName());
                 return;
               }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index c605951..5a60483 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -87,7 +87,7 @@
           .addProperty("internalLateInitProp", JAVA_LANG_STRING, Visibility.INTERNAL)
           .addProperty("publicLateInitProp", JAVA_LANG_STRING, Visibility.PUBLIC);
 
-  private Consumer<InternalOptions> disableAggressiveClassOptimizations =
+  private final Consumer<InternalOptions> disableAggressiveClassOptimizations =
       o -> {
         o.enableClassInlining = false;
         o.enableVerticalClassMerging = false;
@@ -116,13 +116,9 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
-            inspector -> {
-              checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
-            });
+            inspector -> checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName()));
   }
 
   @Test
@@ -132,21 +128,20 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+                return;
+              }
+
               ClassSubject classSubject =
                   checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
               String propertyName = "privateProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
-              if (!allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
 
               // Private property has no getter or setter.
               checkMethodIsAbsent(
@@ -163,11 +158,14 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+                return;
+              }
+
               ClassSubject classSubject =
                   checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
               String propertyName = "protectedProp";
@@ -176,13 +174,8 @@
 
               // Protected property has private field.
               MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-                checkMethodIsRemoved(classSubject, getter);
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(classSubject, getter);
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
+              checkMethodIsKept(classSubject, getter);
             });
   }
 
@@ -193,11 +186,14 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+                return;
+              }
+
               ClassSubject classSubject =
                   checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
               String propertyName = "internalProp";
@@ -206,13 +202,8 @@
 
               // Internal property has private field
               MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-                checkMethodIsRemoved(classSubject, getter);
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(classSubject, getter);
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
+              checkMethodIsKept(classSubject, getter);
             });
   }
 
@@ -223,11 +214,14 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+                return;
+              }
+
               ClassSubject classSubject =
                   checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
               String propertyName = "publicProp";
@@ -236,13 +230,8 @@
 
               // Public property has private field
               MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-                checkMethodIsRemoved(classSubject, getter);
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(classSubject, getter);
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
+              checkMethodIsKept(classSubject, getter);
             });
   }
 
@@ -256,6 +245,11 @@
             testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+                return;
+              }
+
               ClassSubject classSubject =
                   checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
               String propertyName = "primitiveProp";
@@ -263,15 +257,9 @@
 
               MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
               MethodSignature setter = MUTABLE_PROPERTY_CLASS.getSetterForProperty(propertyName);
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-                checkMethodIsRemoved(classSubject, getter);
-                checkMethodIsRemoved(classSubject, setter);
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(classSubject, getter);
-                checkMethodIsRemoved(classSubject, setter);
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
+              checkMethodIsKept(classSubject, getter);
+              checkMethodIsRemoved(classSubject, setter);
             });
   }
 
@@ -284,9 +272,7 @@
             mainClass,
             testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
-            inspector -> {
-              checkClassIsRemoved(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
-            });
+            inspector -> checkClassIsRemoved(inspector, LATE_INIT_PROPERTY_CLASS.getClassName()));
   }
 
   @Test
@@ -296,19 +282,20 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
+                return;
+              }
+
               ClassSubject classSubject =
                   checkClassIsKept(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
               String propertyName = "privateLateInitProp";
               FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
               assertTrue("Field is absent", fieldSubject.isPresent());
-              if (!allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
 
               // Private late init property have no getter or setter.
               checkMethodIsAbsent(
@@ -325,24 +312,9 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
-            inspector -> {
-              ClassSubject classSubject =
-                  checkClassIsKept(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
-              String propertyName = "protectedLateInitProp";
-              FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
-              assertTrue("Field is absent", fieldSubject.isPresent());
-              if (!allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isProtected());
-              }
-
-              // Protected late init property have protected getter
-              checkMethodIsRemoved(
-                  classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
-            });
+            inspector -> checkClassIsRemoved(inspector, LATE_INIT_PROPERTY_CLASS.getClassName()));
   }
 
   @Test
@@ -352,22 +324,9 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
-            inspector -> {
-              ClassSubject classSubject =
-                  checkClassIsKept(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
-              String propertyName = "internalLateInitProp";
-              FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
-              assertTrue("Field is absent", fieldSubject.isPresent());
-              assertTrue(fieldSubject.getField().accessFlags.isPublic());
-
-              // Internal late init property have protected getter
-              checkMethodIsRemoved(
-                  classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
-            });
+            inspector -> checkClassIsRemoved(inspector, LATE_INIT_PROPERTY_CLASS.getClassName()));
   }
 
   @Test
@@ -377,22 +336,9 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
-            inspector -> {
-              ClassSubject classSubject =
-                  checkClassIsKept(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
-              String propertyName = "publicLateInitProp";
-              FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
-              assertTrue("Field is absent", fieldSubject.isPresent());
-              assertTrue(fieldSubject.getField().accessFlags.isPublic());
-
-              // Internal late init property have protected getter
-              checkMethodIsRemoved(
-                  classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
-            });
+            inspector -> checkClassIsRemoved(inspector, LATE_INIT_PROPERTY_CLASS.getClassName()));
   }
 
   @Test
@@ -404,9 +350,8 @@
             mainClass,
             testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
-            inspector -> {
-              checkClassIsRemoved(inspector, USER_DEFINED_PROPERTY_CLASS.getClassName());
-            });
+            inspector ->
+                checkClassIsRemoved(inspector, USER_DEFINED_PROPERTY_CLASS.getClassName()));
   }
 
   @Test
@@ -419,6 +364,11 @@
             testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, USER_DEFINED_PROPERTY_CLASS.getClassName());
+                return;
+              }
+
               ClassSubject classSubject =
                   checkClassIsKept(inspector, USER_DEFINED_PROPERTY_CLASS.getClassName());
               String propertyName = "durationInSeconds";
@@ -430,13 +380,8 @@
                   checkFieldIsKept(classSubject, "int", "durationInMilliSeconds");
               MethodSignature getter =
                   USER_DEFINED_PROPERTY_CLASS.getGetterForProperty(propertyName);
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-                checkMethodIsRemoved(classSubject, getter);
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(classSubject, getter);
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
+              checkMethodIsKept(classSubject, getter);
             });
   }
 
@@ -447,22 +392,22 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              checkClassIsRemoved(inspector, COMPANION_PROPERTY_CLASS.getClassName());
+
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, "properties.CompanionProperties");
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, "properties.CompanionProperties");
-              checkClassIsRemoved(inspector, COMPANION_PROPERTY_CLASS.getClassName());
               String propertyName = "primitiveProp";
               FieldSubject fieldSubject = checkFieldIsKept(outerClass, "int", propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isStatic());
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -473,34 +418,29 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              checkClassIsRemoved(inspector, COMPANION_PROPERTY_CLASS.getClassName());
+
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, "properties.CompanionProperties");
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, "properties.CompanionProperties");
-              checkClassIsRemoved(inspector, COMPANION_PROPERTY_CLASS.getClassName());
               String propertyName = "privateProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-
-              MemberNaming.MethodSignature getter =
-                  COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
-              MemberNaming.MethodSignature setter =
-                  COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
+              assertTrue(fieldSubject.getField().isStatic());
 
               // Because the getter/setter are private, they can only be called from another method
               // in the class. If this is an instance method, they will be called on 'this' which is
               // known to be non-null, thus the getter/setter can be inlined if their code is small
               // enough. Because the backing field is private, they will call into an accessor
-              // (static) method. If access relaxation is enabled, this accessor can be removed.
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              // (static) method.
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -511,23 +451,22 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, "properties.CompanionProperties");
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, "properties.CompanionProperties");
               checkClassIsRemoved(inspector, COMPANION_PROPERTY_CLASS.getClassName());
               String propertyName = "internalProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isStatic());
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -538,23 +477,23 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              checkClassIsRemoved(inspector, COMPANION_PROPERTY_CLASS.getClassName());
+
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, "properties.CompanionProperties");
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, "properties.CompanionProperties");
-              checkClassIsRemoved(inspector, COMPANION_PROPERTY_CLASS.getClassName());
               String propertyName = "publicProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isStatic());
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -566,29 +505,29 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              checkClassIsRemoved(inspector, testedClass.getClassName());
+
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+                return;
+              }
+
               ClassSubject outerClass =
                   checkClassIsKept(inspector, testedClass.getOuterClassName());
-              checkClassIsRemoved(inspector, testedClass.getClassName());
               String propertyName = "privateLateInitProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               // Because the getter/setter are private, they can only be called from another method
               // in the class. If this is an instance method, they will be called on 'this' which is
               // known to be non-null, thus the getter/setter can be inlined if their code is small
               // enough. Because the backing field is private, they will call into an accessor
               // (static) method. If access relaxation is enabled, this accessor can be removed.
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -600,19 +539,11 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
-              ClassSubject outerClass =
-                  checkClassIsKept(inspector, testedClass.getOuterClassName());
               checkClassIsRemoved(inspector, testedClass.getClassName());
-              String propertyName = "internalLateInitProp";
-              FieldSubject fieldSubject =
-                  checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-              assertTrue(fieldSubject.getField().accessFlags.isPublic());
+              checkClassIsRemoved(inspector, testedClass.getOuterClassName());
             });
   }
 
@@ -624,19 +555,11 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
-              ClassSubject outerClass =
-                  checkClassIsKept(inspector, testedClass.getOuterClassName());
               checkClassIsRemoved(inspector, testedClass.getClassName());
-              String propertyName = "publicLateInitProp";
-              FieldSubject fieldSubject =
-                  checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-              assertTrue(fieldSubject.getField().accessFlags.isPublic());
+              checkClassIsRemoved(inspector, testedClass.getClassName());
             });
   }
 
@@ -651,30 +574,21 @@
             testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "primitiveProp";
               FieldSubject fieldSubject = checkFieldIsKept(objectClass, "int", propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
               MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
-
-              if (allowAccessModification) {
-                // Getter and setter is inlined because the constructor of ObjectProperties is
-                // considered trivial, which implies that the member value propagation marks the
-                // INSTANCE field as being non-null.
-                checkMethodIsRemoved(objectClass, getter);
-                checkMethodIsRemoved(objectClass, setter);
-              } else {
-                checkMethodIsKept(objectClass, getter);
-                checkMethodIsRemoved(objectClass, setter);
-              }
-
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              checkMethodIsKept(objectClass, getter);
+              checkMethodIsRemoved(objectClass, setter);
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -686,16 +600,19 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "privateProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
               MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
@@ -704,11 +621,7 @@
               checkMethodIsAbsent(objectClass, getter);
               checkMethodIsAbsent(objectClass, setter);
 
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -723,31 +636,24 @@
             testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "internalProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
               MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
 
-              if (allowAccessModification) {
-                // Getter and setter is inlined because the constructor of ObjectProperties is
-                // considered trivial, which implies that the member value propagation marks the
-                // INSTANCE field as being non-null.
-                checkMethodIsRemoved(objectClass, getter);
-                checkMethodIsRemoved(objectClass, setter);
-              } else {
-                checkMethodIsKept(objectClass, getter);
-                checkMethodIsRemoved(objectClass, setter);
-              }
+              checkMethodIsKept(objectClass, getter);
+              checkMethodIsRemoved(objectClass, setter);
 
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -762,31 +668,24 @@
             testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "publicProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
               MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
 
-              if (allowAccessModification) {
-                // Getter and setter is inlined because the constructor of ObjectProperties is
-                // considered trivial, which implies that the member value propagation marks the
-                // INSTANCE field as being non-null.
-                checkMethodIsRemoved(objectClass, getter);
-                checkMethodIsRemoved(objectClass, setter);
-              } else {
-                checkMethodIsKept(objectClass, getter);
-                checkMethodIsRemoved(objectClass, setter);
-              }
+              checkMethodIsKept(objectClass, getter);
+              checkMethodIsRemoved(objectClass, setter);
 
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -798,16 +697,19 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "privateLateInitProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
               MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
@@ -816,11 +718,7 @@
               checkMethodIsAbsent(objectClass, getter);
               checkMethodIsAbsent(objectClass, setter);
 
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -832,24 +730,8 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
-        .inspect(
-            inspector -> {
-              ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
-              String propertyName = "internalLateInitProp";
-              FieldSubject fieldSubject =
-                  checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-
-              MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
-              MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
-
-              checkMethodIsRemoved(objectClass, getter);
-              checkMethodIsRemoved(objectClass, setter);
-              assertTrue(fieldSubject.getField().accessFlags.isPublic());
-            });
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+        .inspect(inspector -> checkClassIsRemoved(inspector, testedClass.getClassName()));
   }
 
   @Test
@@ -860,24 +742,8 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
-        .inspect(
-            inspector -> {
-              ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
-              String propertyName = "publicLateInitProp";
-              FieldSubject fieldSubject =
-                  checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-
-              MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
-              MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
-
-              checkMethodIsRemoved(objectClass, getter);
-              checkMethodIsRemoved(objectClass, setter);
-              assertTrue(fieldSubject.getField().accessFlags.isPublic());
-            });
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+        .inspect(inspector -> checkClassIsRemoved(inspector, testedClass.getClassName()));
   }
 
   @Test
@@ -891,23 +757,22 @@
             testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "primitiveProp";
               FieldSubject fieldSubject = checkFieldIsKept(objectClass, "int", propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
               MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
 
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-                checkMethodIsRemoved(objectClass, getter);
-                checkMethodIsRemoved(objectClass, setter);
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(objectClass, getter);
-                checkMethodIsRemoved(objectClass, setter);
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
+              checkMethodIsKept(objectClass, getter);
+              checkMethodIsRemoved(objectClass, setter);
             });
   }
 
@@ -919,16 +784,19 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "privateProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
               MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
@@ -936,11 +804,7 @@
               // A private property has no getter/setter.
               checkMethodIsAbsent(objectClass, getter);
               checkMethodIsAbsent(objectClass, setter);
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -952,16 +816,19 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "internalProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               // We expect getter to be inlined when access (of the backing field) is relaxed to
               // public.
@@ -969,13 +836,8 @@
               // Note: the setter is considered as a regular method (because of KotlinC adding extra
               // null checks), thus we cannot say if the setter would be inlined or not by R8.
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-                checkMethodIsRemoved(objectClass, getter);
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(objectClass, getter);
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
+              checkMethodIsKept(objectClass, getter);
             });
   }
 
@@ -987,29 +849,27 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "publicProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               // We expect getter to be inlined when access (of the backing field) is relaxed to
               // public. On the other hand, the setter is considered as a regular method (because of
               // null checks), thus we cannot say if it can be inlined or not.
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
 
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-                checkMethodIsRemoved(objectClass, getter);
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-                checkMethodIsKept(objectClass, getter);
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
+              checkMethodIsKept(objectClass, getter);
             });
   }
 
@@ -1021,16 +881,19 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
         .inspect(
             inspector -> {
+              if (allowAccessModification) {
+                checkClassIsRemoved(inspector, testedClass.getClassName());
+                return;
+              }
+
               ClassSubject fileClass = checkClassIsKept(inspector, testedClass.getClassName());
               String propertyName = "privateLateInitProp";
               FieldSubject fieldSubject =
                   checkFieldIsKept(fileClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
+              assertTrue(fieldSubject.getField().isStatic());
 
               MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
               MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
@@ -1038,11 +901,7 @@
               // A private property has no getter/setter.
               checkMethodIsAbsent(fileClass, getter);
               checkMethodIsAbsent(fileClass, setter);
-              if (allowAccessModification) {
-                assertTrue(fieldSubject.getField().accessFlags.isPublic());
-              } else {
-                assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-              }
+              assertTrue(fieldSubject.getField().isPrivate());
             });
   }
 
@@ -1054,26 +913,8 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
-        .inspect(
-            inspector -> {
-              ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
-              String propertyName = "internalLateInitProp";
-              FieldSubject fieldSubject =
-                  checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-              assertTrue(fieldSubject.getField().accessFlags.isPublic());
-
-              MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
-              MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
-
-              // Field is public and getter/setter is only called from one place so we expect to
-              // always inline it.
-              checkMethodIsRemoved(objectClass, getter);
-              checkMethodIsRemoved(objectClass, setter);
-            });
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+        .inspect(inspector -> checkClassIsRemoved(inspector, testedClass.getClassName()));
   }
 
   @Test
@@ -1084,24 +925,8 @@
     runTest(
             PACKAGE_NAME,
             mainClass,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(disableAggressiveClassOptimizations))
-        .inspect(
-            inspector -> {
-              ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
-              String propertyName = "publicLateInitProp";
-              FieldSubject fieldSubject =
-                  checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
-              assertTrue(fieldSubject.getField().accessFlags.isStatic());
-
-              MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
-              MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
-
-              checkMethodIsRemoved(objectClass, getter);
-              checkMethodIsRemoved(objectClass, setter);
-              assertTrue(fieldSubject.getField().accessFlags.isPublic());
-            });
+            testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+        .inspect(inspector -> checkClassIsRemoved(inspector, testedClass.getClassName()));
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
index 1652222..1d25958 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -9,7 +9,6 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
@@ -70,9 +69,6 @@
     ClassSubject enumClass = inspector.clazz(ENUM_CLASS_NAME);
     assertThat(enumClass, isPresent());
     assertEquals(minify, enumClass.isRenamed());
-    // TODO(b/179994975): Kotlin enum changed in 1.5.
-    assertThat(
-        enumClass.clinit(),
-        kotlinc.is(KotlinCompilerVersion.KOTLINC_1_5_0) ? isPresent() : isAbsent());
+    assertThat(enumClass.clinit(), isAbsent());
   }
 }