Revert "Unify first and final vertical class merging"

This reverts commit b4a9c8e75bc6bb86efef1f3a606f6e8ecd341136.

Reason for revert: Test failure

Change-Id: I3a785deb69338a4c1afb1aeabeadfa110bf15391
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 3ea094d..65933c8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -716,14 +716,6 @@
     this.kotlinMetadataLens = kotlinMetadataLens;
   }
 
-  public boolean hasInitializedClassesInInstanceMethods() {
-    return initializedClassesInInstanceMethods != null;
-  }
-
-  public InitializedClassesInInstanceMethods getInitializedClassesInInstanceMethods() {
-    return initializedClassesInInstanceMethods;
-  }
-
   public void setInitializedClassesInInstanceMethods(
       InitializedClassesInInstanceMethods initializedClassesInInstanceMethods) {
     this.initializedClassesInInstanceMethods = initializedClassesInInstanceMethods;
@@ -1254,20 +1246,6 @@
                 public boolean shouldRun() {
                   return !appView.cfByteCodePassThrough.isEmpty();
                 }
-              },
-              new ThreadTask() {
-                @Override
-                public void run(Timing timing) {
-                  appView.setInitializedClassesInInstanceMethods(
-                      appView
-                          .getInitializedClassesInInstanceMethods()
-                          .rewrittenWithLens(lens, appliedLens));
-                }
-
-                @Override
-                public boolean shouldRun() {
-                  return appView.hasInitializedClassesInInstanceMethods();
-                }
               });
         });
 
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
index ddb64d7..42ca59e 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -9,11 +9,9 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
-import com.android.tools.r8.utils.MapUtils;
 import java.util.IdentityHashMap;
 import java.util.Map;
 
@@ -52,26 +50,6 @@
       // transitively.
       return !subject.isInterface();
     }
-
-    public InitializedClassesInInstanceMethods rewrittenWithLens(
-        GraphLens lens, GraphLens appliedLens) {
-      return new InitializedClassesInInstanceMethods(
-          appView,
-          MapUtils.transform(
-              mapping,
-              IdentityHashMap::new,
-              key -> {
-                DexType rewrittenKey = lens.lookupType(key, appliedLens);
-                return rewrittenKey.isPrimitiveType() ? null : rewrittenKey;
-              },
-              value -> {
-                DexType rewrittenValue = lens.lookupType(value, appliedLens);
-                return rewrittenValue.isPrimitiveType() ? null : rewrittenValue;
-              },
-              (key, value, otherValue) ->
-                  ClassTypeElement.computeLeastUpperBoundOfClasses(
-                      appView.appInfo(), value, otherValue)));
-    }
   }
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 13ce76b..b4dbe9f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -87,6 +87,7 @@
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMultiNewArray;
 import com.android.tools.r8.ir.code.InvokePolymorphic;
@@ -115,6 +116,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.LazyBox;
 import com.android.tools.r8.verticalclassmerging.InterfaceTypeToClassTypeLensCodeRewriterHelper;
+import com.android.tools.r8.verticalclassmerging.VerticallyMergedClasses;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -395,6 +397,9 @@
                 assert false;
                 continue;
               }
+              if (invoke.isInvokeDirect()) {
+                checkInvokeDirect(method.getReference(), invoke.asInvokeDirect());
+              }
               MethodLookupResult lensLookup =
                   graphLens.lookupMethod(
                       invokedMethod, method.getReference(), invoke.getType(), codeLens);
@@ -1308,6 +1313,54 @@
     return TypeElement.getNull();
   }
 
+  @SuppressWarnings("ReferenceEquality")
+  // If the given invoke is on the form "invoke-direct A.<init>, v0, ..." and the definition of
+  // value v0 is "new-instance v0, B", where B is a subtype of A (see the Art800 and B116282409
+  // tests), then fail with a compilation error if A has previously been merged into B.
+  //
+  // The motivation for this is that the vertical class merger cannot easily recognize the above
+  // code pattern, since it runs prior to IR construction. Therefore, we currently allow merging
+  // A and B although this will lead to invalid code, because this code pattern does generally
+  // not occur in practice (it leads to a verification error on the JVM, but not on Art).
+  private void checkInvokeDirect(DexMethod method, InvokeDirect invoke) {
+    VerticallyMergedClasses verticallyMergedClasses = appView.getVerticallyMergedClasses();
+    if (verticallyMergedClasses == null) {
+      // No need to check the invocation.
+      return;
+    }
+    DexMethod invokedMethod = invoke.getInvokedMethod();
+    if (invokedMethod.name != factory.constructorMethodName) {
+      // Not a constructor call.
+      return;
+    }
+    if (invoke.arguments().isEmpty()) {
+      // The new instance should always be passed to the constructor call, but continue gracefully.
+      return;
+    }
+    Value receiver = invoke.arguments().get(0);
+    if (!receiver.isPhi() && receiver.definition.isNewInstance()) {
+      NewInstance newInstance = receiver.definition.asNewInstance();
+      if (newInstance.clazz != invokedMethod.holder
+          && verticallyMergedClasses.hasBeenMergedIntoSubtype(invokedMethod.holder)) {
+        // Generated code will not work. Fail with a compilation error.
+        throw appView
+            .options()
+            .reporter
+            .fatalError(
+                String.format(
+                    "Unable to rewrite `invoke-direct %s.<init>(new %s, ...)` in method `%s` after "
+                        + "type `%s` was merged into `%s`. Please add the following rule to your "
+                        + "Proguard configuration file: `-keep,allowobfuscation class %s`.",
+                    invokedMethod.holder.toSourceString(),
+                    newInstance.clazz,
+                    method.toSourceString(),
+                    invokedMethod.holder,
+                    verticallyMergedClasses.getTargetFor(invokedMethod.holder),
+                    invokedMethod.holder.toSourceString()));
+      }
+    }
+  }
+
   /**
    * Due to class merging, it is possible that two exception classes have been merged into one. This
    * function removes catch handlers where the guards ended up being the same as a previous one.
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index 3ae9270..1e63e51 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -33,13 +33,13 @@
 import com.android.tools.r8.shaking.KeepReason.ReflectiveUseFrom;
 import com.android.tools.r8.utils.DequeUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.SetUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.Sets;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
+import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
@@ -532,12 +532,14 @@
     }
     if (rule instanceof ProguardIfRule) {
       ProguardIfRule ifRule = (ProguardIfRule) rule;
-      assert ifRule.getPrecondition() != null;
+      assert !ifRule.getPreconditions().isEmpty();
       return ruleNodes.computeIfAbsent(
           ifRule,
           key -> {
-            Set<GraphNode> preconditions =
-                SetUtils.newHashSet(getGraphNode(ifRule.getPrecondition().getReference()));
+            Set<GraphNode> preconditions = new HashSet<>(ifRule.getPreconditions().size());
+            for (DexReference condition : ifRule.getPreconditions()) {
+              preconditions.add(getGraphNode(condition));
+            }
             return new KeepRuleGraphNode(ifRule, preconditions);
           });
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 44ba57d..32ec312 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -12,14 +14,17 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.shaking.InlineRule.InlineRuleType;
+import com.android.tools.r8.shaking.InlineRule.Type;
 import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSet;
 import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSetBuilder;
 import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
 import com.android.tools.r8.threading.TaskCollection;
 import com.android.tools.r8.utils.InternalOptions.TestingOptions.ProguardIfRuleEvaluationData;
 import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -93,12 +98,48 @@
                 if (appView.options().testing.measureProguardIfRuleEvaluations) {
                   ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
                 }
-                boolean matched = evaluateIfRuleMembersAndMaterialize(ifRule, clazz);
+                boolean matched = evaluateIfRuleMembersAndMaterialize(ifRule, clazz, clazz);
                 if (matched && canRemoveSubsequentKeepRule(ifRule)) {
                   toRemove.add(ifRule);
                 }
               }
             }
+
+            // Check if one of the types that have been merged into `clazz` satisfies the if-rule.
+            if (appView.getVerticallyMergedClasses() != null) {
+              Iterable<DexType> sources =
+                  appView.getVerticallyMergedClasses().getSourcesFor(clazz.type);
+              for (DexType sourceType : sources) {
+                // Note that, although `sourceType` has been merged into `type`, the dex class for
+                // `sourceType` is still available until the second round of tree shaking. This
+                // way we can still retrieve the access flags of `sourceType`.
+                DexProgramClass sourceClass =
+                    asProgramClassOrNull(
+                        appView.appInfo().definitionForWithoutExistenceAssert(sourceType));
+                if (sourceClass == null) {
+                  // TODO(b/266049507): The evaluation of -if rules in the final round of tree
+                  //  shaking and during -whyareyoukeeping should be the same. Currently the pruning
+                  //  of classes changes behavior.
+                  assert enqueuer.getMode().isWhyAreYouKeeping();
+                  continue;
+                }
+                if (appView.options().testing.measureProguardIfRuleEvaluations) {
+                  ifRuleEvaluationData.numberOfProguardIfRuleClassEvaluations++;
+                }
+                if (evaluateClassForIfRule(ifRuleKey, sourceClass)) {
+                  for (ProguardIfRule ifRule : ifRulesInEquivalence) {
+                    registerClassCapture(ifRule, sourceClass, clazz);
+                    if (appView.options().testing.measureProguardIfRuleEvaluations) {
+                      ifRuleEvaluationData.numberOfProguardIfRuleMemberEvaluations++;
+                    }
+                    if (evaluateIfRuleMembersAndMaterialize(ifRule, sourceClass, clazz)
+                        && canRemoveSubsequentKeepRule(ifRule)) {
+                      toRemove.add(ifRule);
+                    }
+                  }
+                }
+              }
+            }
           }
           if (ifRulesInEquivalence.size() == toRemove.size()) {
             it.remove();
@@ -162,11 +203,12 @@
     return true;
   }
 
-  private boolean evaluateIfRuleMembersAndMaterialize(ProguardIfRule rule, DexProgramClass clazz)
-      throws ExecutionException {
+  @SuppressWarnings("ReferenceEquality")
+  private boolean evaluateIfRuleMembersAndMaterialize(
+      ProguardIfRule rule, DexClass sourceClass, DexClass targetClass) throws ExecutionException {
     Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
     if (memberKeepRules.isEmpty()) {
-      materializeIfRule(rule, clazz);
+      materializeIfRule(rule, ImmutableSet.of(sourceClass.getReference()));
       return true;
     }
 
@@ -174,12 +216,12 @@
     Set<DexDefinition> filteredMembers = Sets.newIdentityHashSet();
     Iterables.addAll(
         filteredMembers,
-        clazz.fields(
+        targetClass.fields(
             f -> {
               // Fields that are javac inlined are unsound as predicates for conditional rules.
               // Ignore any such field members and record it for possible reporting later.
               if (isFieldInlinedByJavaC(f)) {
-                fieldsInlinedByJavaC.add(DexClassAndField.create(clazz, f));
+                fieldsInlinedByJavaC.add(DexClassAndField.create(targetClass, f));
                 return false;
               }
               // Fields referenced only by -keep may not be referenced, we therefore have to
@@ -187,24 +229,18 @@
               return (enqueuer.isFieldLive(f)
                       || enqueuer.isFieldReferenced(f)
                       || f.getOptimizationInfo().valueHasBeenPropagated())
-                  && appView
-                      .graphLens()
-                      .getOriginalFieldSignature(f.getReference())
-                      .getHolderType()
-                      .isIdenticalTo(clazz.getType());
+                  && (appView.graphLens().getOriginalFieldSignature(f.getReference()).holder
+                      == sourceClass.type);
             }));
     Iterables.addAll(
         filteredMembers,
-        clazz.methods(
+        targetClass.methods(
             m ->
                 (enqueuer.isMethodLive(m)
                         || enqueuer.isMethodTargeted(m)
                         || m.getOptimizationInfo().returnValueHasBeenPropagated())
-                    && appView
-                        .graphLens()
-                        .getOriginalMethodSignature(m.getReference())
-                        .getHolderType()
-                        .isIdenticalTo(clazz.getType())));
+                    && appView.graphLens().getOriginalMethodSignature(m.getReference()).holder
+                        == sourceClass.type));
 
     // Check if the rule could hypothetically have matched a javac inlined field.
     // If so mark the rule. Reporting happens only if the rule is otherwise unused.
@@ -235,11 +271,11 @@
         Sets.combinations(filteredMembers, memberKeepRules.size())) {
       Collection<DexClassAndField> fieldsInCombination =
           DexDefinition.filterDexEncodedField(
-                  combination.stream(), field -> DexClassAndField.create(clazz, field))
+                  combination.stream(), field -> DexClassAndField.create(targetClass, field))
               .collect(Collectors.toList());
       Collection<DexClassAndMethod> methodsInCombination =
           DexDefinition.filterDexEncodedMethod(
-                  combination.stream(), method -> DexClassAndMethod.create(clazz, method))
+                  combination.stream(), method -> DexClassAndMethod.create(targetClass, method))
               .collect(Collectors.toList());
       // Member rules are combined as AND logic: if found unsatisfied member rule, this
       // combination of live members is not a good fit.
@@ -251,7 +287,7 @@
                           || rootSetBuilder.ruleSatisfiedByMethods(
                               memberRule, methodsInCombination));
       if (satisfied) {
-        materializeIfRule(rule, clazz);
+        materializeIfRule(rule, ImmutableSet.of(sourceClass.getReference()));
         if (canRemoveSubsequentKeepRule(rule)) {
           return true;
         }
@@ -269,17 +305,25 @@
     return field.getOrComputeIsInlinableByJavaC(appView.dexItemFactory());
   }
 
-  private void materializeIfRule(ProguardIfRule rule, DexProgramClass precondition)
+  @SuppressWarnings("BadImport")
+  private void materializeIfRule(ProguardIfRule rule, Set<DexReference> preconditions)
       throws ExecutionException {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
-    ProguardIfRule materializedRule = rule.materialize(dexItemFactory, precondition);
+    ProguardIfRule materializedRule = rule.materialize(dexItemFactory, preconditions);
 
     if (enqueuer.getMode().isInitialTreeShaking()
         && !rule.isUsed()
         && !rule.isTrivalAllClassMatch()) {
+      // We need to abort class inlining of classes that could be matched by the condition of this
+      // -if rule.
+      ClassInlineRule neverClassInlineRuleForCondition =
+          materializedRule.neverClassInlineRuleForCondition(dexItemFactory);
+      if (neverClassInlineRuleForCondition != null) {
+        rootSetBuilder.runPerRule(tasks, neverClassInlineRuleForCondition, null);
+      }
+
       InlineRule neverInlineForClassInliningRuleForCondition =
-          materializedRule.neverInlineRuleForCondition(
-              dexItemFactory, InlineRuleType.NEVER_CLASS_INLINE);
+          materializedRule.neverInlineRuleForCondition(dexItemFactory, Type.NEVER_CLASS_INLINE);
       if (neverInlineForClassInliningRuleForCondition != null) {
         rootSetBuilder.runPerRule(tasks, neverInlineForClassInliningRuleForCondition, null);
       }
@@ -288,18 +332,17 @@
       // ensure that the subsequent rule will be applied again in the second round of tree
       // shaking.
       InlineRule neverInlineRuleForCondition =
-          materializedRule.neverInlineRuleForCondition(dexItemFactory, InlineRuleType.NEVER);
+          materializedRule.neverInlineRuleForCondition(dexItemFactory, Type.NEVER);
       if (neverInlineRuleForCondition != null) {
         rootSetBuilder.runPerRule(tasks, neverInlineRuleForCondition, null);
       }
 
-      rootSetBuilder
-          .getDependentMinimumKeepInfo()
-          .getOrCreateUnconditionalMinimumKeepInfoFor(precondition.getType())
-          .asClassJoiner()
-          .disallowClassInlining()
-          .disallowHorizontalClassMerging()
-          .disallowVerticalClassMerging();
+      // Prevent horizontal class merging of any -if rule members.
+      NoHorizontalClassMergingRule noHorizontalClassMergingRule =
+          materializedRule.noHorizontalClassMergingRuleForCondition(dexItemFactory);
+      if (noHorizontalClassMergingRule != null) {
+        rootSetBuilder.runPerRule(tasks, noHorizontalClassMergingRule, null);
+      }
     }
 
     // Keep whatever is required by the -if rule.
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index 4949e5a..cc813ad 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -10,7 +10,7 @@
 
 public class InlineRule extends ProguardConfigurationRule {
 
-  public enum InlineRuleType {
+  public enum Type {
     ALWAYS,
     NEVER,
     NEVER_CLASS_INLINE,
@@ -24,14 +24,14 @@
       super();
     }
 
-    InlineRuleType type;
+    Type type;
 
     @Override
     public Builder self() {
       return this;
     }
 
-    public Builder setType(InlineRuleType type) {
+    public Builder setType(Type type) {
       this.type = type;
       return this;
     }
@@ -56,7 +56,7 @@
     }
   }
 
-  private final InlineRuleType type;
+  private final Type type;
 
   protected InlineRule(
       Origin origin,
@@ -72,7 +72,7 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules,
-      InlineRuleType type) {
+      Type type) {
     super(
         origin,
         position,
@@ -94,7 +94,7 @@
     return new Builder();
   }
 
-  public InlineRuleType getType() {
+  public Type getType() {
     return type;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index b2dcea5..6ae7d4c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.position.TextPosition;
 import com.android.tools.r8.position.TextRange;
-import com.android.tools.r8.shaking.InlineRule.InlineRuleType;
 import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
 import com.android.tools.r8.shaking.ProguardWildcard.BackReference;
@@ -465,7 +464,7 @@
       } else if (acceptString("alwaysinline")) {
         InlineRule rule =
             parseRuleWithClassSpec(
-                optionStart, InlineRule.builder().setType(InlineRuleType.ALWAYS));
+                optionStart, InlineRule.builder().setType(InlineRule.Type.ALWAYS));
         configurationBuilder.addRule(rule);
       } else if (acceptString("adaptclassstrings")) {
         parseClassFilter(configurationBuilder::addAdaptClassStringsPattern);
@@ -567,14 +566,14 @@
         if (acceptString("neverinline")) {
           InlineRule rule =
               parseRuleWithClassSpec(
-                  optionStart, InlineRule.builder().setType(InlineRuleType.NEVER));
+                  optionStart, InlineRule.builder().setType(InlineRule.Type.NEVER));
           configurationBuilder.addRule(rule);
           return true;
         }
         if (acceptString("neversinglecallerinline")) {
           InlineRule rule =
               parseRuleWithClassSpec(
-                  optionStart, InlineRule.builder().setType(InlineRuleType.NEVER_SINGLE_CALLER));
+                  optionStart, InlineRule.builder().setType(InlineRule.Type.NEVER_SINGLE_CALLER));
           configurationBuilder.addRule(rule);
           return true;
         }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index cb3757c..2279a8e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -5,10 +5,9 @@
 
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
-import com.android.tools.r8.shaking.InlineRule.InlineRuleType;
 import com.google.common.collect.Iterables;
 import java.util.List;
 import java.util.Map;
@@ -26,14 +25,21 @@
         }
       };
 
-  private final DexProgramClass precondition;
+  private static final Origin NO_HORIZONTAL_CLASS_MERGING_ORIGIN =
+      new Origin(Origin.root()) {
+        @Override
+        public String part() {
+          return "<SYNTHETIC_NO_HORIZONTAL_CLASS_MERGING_RULE>";
+        }
+      };
+
+  private final Set<DexReference> preconditions;
   final ProguardKeepRule subsequentRule;
 
   private Map<DexField, DexField> inlinableFieldsInPrecondition = new ConcurrentHashMap<>();
 
-  public DexProgramClass getPrecondition() {
-    assert precondition != null;
-    return precondition;
+  public Set<DexReference> getPreconditions() {
+    return preconditions;
   }
 
   public ProguardKeepRule getSubsequentRule() {
@@ -106,7 +112,7 @@
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules,
       ProguardKeepRule subsequentRule,
-      DexProgramClass precondition) {
+      Set<DexReference> preconditions) {
     super(
         origin,
         position,
@@ -124,7 +130,7 @@
         ProguardKeepRuleType.CONDITIONAL,
         ProguardKeepRuleModifiers.builder().build());
     this.subsequentRule = subsequentRule;
-    this.precondition = precondition;
+    this.preconditions = preconditions;
   }
 
   public static Builder builder() {
@@ -147,7 +153,7 @@
   }
 
   protected ProguardIfRule materialize(
-      DexItemFactory dexItemFactory, DexProgramClass precondition) {
+      DexItemFactory dexItemFactory, Set<DexReference> preconditions) {
     return new ProguardIfRule(
         getOrigin(),
         getPosition(),
@@ -169,7 +175,27 @@
                 .map(memberRule -> memberRule.materialize(dexItemFactory))
                 .collect(Collectors.toList()),
         subsequentRule.materialize(dexItemFactory),
-        precondition);
+        preconditions);
+  }
+
+  protected ClassInlineRule neverClassInlineRuleForCondition(DexItemFactory dexItemFactory) {
+    return new ClassInlineRule(
+        NEVER_INLINE_ORIGIN,
+        Position.UNKNOWN,
+        null,
+        ProguardTypeMatcher.materializeList(getClassAnnotations(), dexItemFactory),
+        getClassAccessFlags(),
+        getNegatedClassAccessFlags(),
+        getClassTypeNegated(),
+        getClassType(),
+        getClassNames().materialize(dexItemFactory),
+        ProguardTypeMatcher.materializeList(getInheritanceAnnotations(), dexItemFactory),
+        getInheritanceClassName() == null
+            ? null
+            : getInheritanceClassName().materialize(dexItemFactory),
+        getInheritanceIsExtends(),
+        getMemberRules(),
+        ClassInlineRule.Type.NEVER);
   }
 
   /**
@@ -195,7 +221,7 @@
    * -neverinline rule for the condition of the -if rule.
    */
   protected InlineRule neverInlineRuleForCondition(
-      DexItemFactory dexItemFactory, InlineRuleType type) {
+      DexItemFactory dexItemFactory, InlineRule.Type type) {
     if (getMemberRules() == null || getMemberRules().isEmpty()) {
       return null;
     }
@@ -221,6 +247,36 @@
         type);
   }
 
+  protected NoHorizontalClassMergingRule noHorizontalClassMergingRuleForCondition(
+      DexItemFactory dexItemFactory) {
+    List<ProguardMemberRule> memberRules = null;
+    if (getMemberRules() != null) {
+      memberRules =
+          getMemberRules().stream()
+              .map(memberRule -> memberRule.materialize(dexItemFactory))
+              .collect(Collectors.toList());
+    }
+
+    return NoHorizontalClassMergingRule.builder()
+        .setOrigin(NO_HORIZONTAL_CLASS_MERGING_ORIGIN)
+        .addClassAnnotations(
+            ProguardTypeMatcher.materializeList(getClassAnnotations(), dexItemFactory))
+        .setClassAccessFlags(getClassAccessFlags())
+        .setNegatedClassAccessFlags(getNegatedClassAccessFlags())
+        .setClassType(getClassType())
+        .setClassTypeNegated(getClassTypeNegated())
+        .setClassNames(getClassNames().materialize(dexItemFactory))
+        .addInheritanceAnnotations(
+            ProguardTypeMatcher.materializeList(getInheritanceAnnotations(), dexItemFactory))
+        .setInheritanceClassName(
+            getInheritanceClassName() == null
+                ? null
+                : getInheritanceClassName().materialize(dexItemFactory))
+        .setInheritanceIsExtends(getInheritanceIsExtends())
+        .setMemberRules(memberRules)
+        .build();
+  }
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardIfRule)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 9d30ea3..cecee47 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -173,10 +173,6 @@
           null);
     }
 
-    public DependentMinimumKeepInfoCollection getDependentMinimumKeepInfo() {
-      return dependentMinimumKeepInfo;
-    }
-
     boolean isMainDexRootSetBuilder() {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index c991a7d..ed49f66 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -122,9 +122,6 @@
             return;
           }
           V2 newValue = valueMapping.apply(key, value);
-          if (newValue == null) {
-            return;
-          }
           V2 existingValue = result.put(newKey, newValue);
           if (existingValue != null) {
             result.put(newKey, valueMerger.apply(newKey, existingValue, newValue));
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java b/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
index f852310..d8856f9 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.InvokeType;
@@ -33,7 +34,7 @@
 
 /**
  * A short-lived piece of code that will be converted into {@link LirCode} using {@link
- * #toLirCode(AppView, VerticalClassMergerGraphLens, ClassMergerMode)}.
+ * #toLirCode(AppView)}.
  */
 public class IncompleteVerticalClassMergerBridgeCode extends Code {
 
@@ -86,7 +87,10 @@
     method = lens.getNextBridgeMethodSignature(method);
   }
 
-  public LirCode<?> toLirCode(AppView<AppInfoWithLiveness> appView) {
+  public LirCode<?> toLirCode(
+      AppView<AppInfoWithLiveness> appView,
+      VerticalClassMergerGraphLens lens,
+      ClassMergerMode mode) {
     boolean isD8R8Synthesized = true;
     LirEncodingStrategy<Value, Integer> strategy =
         LirStrategy.getDefaultStrategy().getEncodingStrategy();
@@ -124,7 +128,21 @@
       lirBuilder.addReturn(returnValue);
     }
 
-    return lirBuilder.build();
+    LirCode<Integer> lirCode = lirBuilder.build();
+    return mode.isFinal()
+        ? lirCode
+        : new LirCode<>(lirCode) {
+
+          @Override
+          public boolean hasExplicitCodeLens() {
+            return true;
+          }
+
+          @Override
+          public GraphLens getCodeLens(AppView<?> appView) {
+            return lens;
+          }
+        };
   }
 
   // Implement Code.
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index 6772ced..943f954 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -143,11 +143,10 @@
     rewriteCodeWithLens(executorService, timing);
 
     // Remove merged classes from app now that the code is fully rewritten.
-    removeMergedClasses(
-        verticalClassMergerResult.getVerticallyMergedClasses(), executorService, timing);
+    removeMergedClasses(verticalClassMergerResult.getVerticallyMergedClasses(), timing);
 
     // Convert the (incomplete) synthesized bridges to LIR.
-    finalizeSynthesizedBridges(verticalClassMergerResult.getSynthesizedBridges(), timing);
+    finalizeSynthesizedBridges(verticalClassMergerResult.getSynthesizedBridges(), lens, timing);
 
     // Finally update the code lens to signal that the code is fully up to date.
     markRewrittenWithLens(executorService, timing);
@@ -248,6 +247,9 @@
 
   private void rewriteCodeWithLens(ExecutorService executorService, Timing timing)
       throws ExecutionException {
+    if (mode.isInitial()) {
+      return;
+    }
     LirConverter.rewriteLirWithLens(appView, timing, executorService);
     new IdentifierMinifier(appView).rewriteDexItemBasedConstStringInStaticFields(executorService);
   }
@@ -290,14 +292,11 @@
     timing.end();
   }
 
-  private void removeMergedClasses(
-      VerticallyMergedClasses verticallyMergedClasses,
-      ExecutorService executorService,
-      Timing timing)
-      throws ExecutionException {
+  private void removeMergedClasses(VerticallyMergedClasses verticallyMergedClasses, Timing timing) {
     if (mode.isInitial()) {
       return;
     }
+
     timing.begin("Remove merged classes");
     DirectMappedDexApplication newApplication =
         appView
@@ -306,17 +305,13 @@
             .builder()
             .removeProgramClasses(clazz -> verticallyMergedClasses.isMergeSource(clazz.getType()))
             .build();
-    PrunedItems prunedItems =
-        PrunedItems.builder()
-            .addRemovedClasses(verticallyMergedClasses.getSources())
-            .setPrunedApp(newApplication)
-            .build();
-    appView.setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems, executorService, timing));
+    appView.setAppInfo(appView.appInfo().rebuildWithLiveness(newApplication));
     timing.end();
   }
 
   private void finalizeSynthesizedBridges(
       List<IncompleteVerticalClassMergerBridgeCode> bridges,
+      VerticalClassMergerGraphLens lens,
       Timing timing) {
     timing.begin("Finalize synthesized bridges");
     KeepInfoCollection keepInfo = appView.getKeepInfo();
@@ -328,7 +323,7 @@
       assert target != null;
 
       // Finalize code.
-      bridge.setCode(code.toLirCode(appView), appView);
+      bridge.setCode(code.toLirCode(appView, lens, mode), appView);
 
       // Copy keep info to newly synthesized methods.
       keepInfo.mutate(
@@ -340,6 +335,9 @@
 
   private void markRewrittenWithLens(ExecutorService executorService, Timing timing)
       throws ExecutionException {
+    if (mode.isInitial()) {
+      return;
+    }
     timing.begin("Mark rewritten with lens");
     appView.clearCodeRewritings(executorService, timing);
     timing.end();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/B116282409.java b/src/test/java/com/android/tools/r8/ir/optimize/B116282409.java
index 0031854..6876898 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/B116282409.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/B116282409.java
@@ -4,20 +4,24 @@
 
 package com.android.tools.r8.ir.optimize;
 
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
-import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertFalse;
 
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
 import com.android.tools.r8.jasmin.JasminTestBase;
+import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
 import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
@@ -101,36 +105,79 @@
 
   @Test
   public void testR8() throws Exception {
-    testForR8(parameters.getBackend())
-        .addProgramClassFileData(programClassFileData)
-        .addKeepMainRule("TestClass")
-        .addOptionsModification(
-            options ->
-                options.getVerticalClassMergerOptions().setEnabled(enableVerticalClassMerging))
-        .allowDiagnosticWarningMessages()
-        .setMinApi(parameters)
-        .compileWithExpectedDiagnostics(
-            diagnostics ->
-                diagnostics.assertWarningsMatch(
-                    allOf(
-                        diagnosticType(UnverifiableCfCodeDiagnostic.class),
-                        diagnosticMessage(
-                            containsString(
-                                "Constructor mismatch, expected constructor from B, but was"
-                                    + " A.<init>()")))))
+    if (enableVerticalClassMerging) {
+      exception.expect(CompilationFailedException.class);
+      exception.expectCause(
+          new CustomExceptionMatcher(
+              "Unable to rewrite `invoke-direct A.<init>(new B, ...)` in method "
+                  + "`void TestClass.main(java.lang.String[])` after type `A` was merged into `B`.",
+              "Please add the following rule to your Proguard configuration file: "
+                  + "`-keep,allowobfuscation class A`."));
+    }
+
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addProgramClassFileData(programClassFileData)
+            .addKeepMainRule("TestClass")
+            .addOptionsModification(
+                options ->
+                    options.getVerticalClassMergerOptions().setEnabled(enableVerticalClassMerging))
+            .allowDiagnosticWarningMessages()
+            .setMinApi(parameters)
+            .compile();
+
+    assertFalse(enableVerticalClassMerging);
+
+    compileResult
         .run(parameters.getRuntime(), "TestClass")
         .applyIf(
-            (parameters.isCfRuntime() || parameters.getDexRuntimeVersion().isDalvik())
-                && !enableVerticalClassMerging,
+            parameters.isCfRuntime(),
             runResult ->
                 runResult
                     .assertFailureWithErrorThatThrows(VerifyError.class)
                     .assertFailureWithErrorThatMatches(
-                        containsString(
-                            parameters.isCfRuntime()
-                                ? "Call to wrong initialization method"
-                                : "VFY: invoke-direct <init> on super only allowed for 'this' in"
-                                    + " <init>")),
-            runResult -> runResult.assertSuccessWithOutputLines("In A.<init>()", "42"));
+                        containsString("Call to wrong initialization method")),
+            runResult -> {
+              if (parameters.getDexRuntimeVersion().isDalvik()) {
+                runResult.assertFailureWithErrorThatMatches(
+                    containsString(
+                        "VFY: invoke-direct <init> on super only allowed for 'this' in <init>"));
+              } else {
+                runResult.assertSuccessWithOutputLines("In A.<init>()", "42");
+              }
+            });
+  }
+
+  private static class CustomExceptionMatcher extends BaseMatcher<Throwable> {
+
+    private List<String> messages;
+
+    public CustomExceptionMatcher(String... messages) {
+      this.messages = Arrays.asList(messages);
+    }
+
+    @Override
+    public void describeTo(Description description) {
+      description
+          .appendText("a string containing ")
+          .appendText(
+              String.join(
+                  ", ", messages.stream().map(m -> "\"" + m + "\"").collect(Collectors.toList())));
+    }
+
+    @Override
+    public boolean matches(Object o) {
+      if (o instanceof AbortException) {
+        AbortException exception = (AbortException) o;
+        if (exception.getMessage() != null
+            && messages.stream().allMatch(message -> exception.getMessage().contains(message))) {
+          return true;
+        }
+        if (exception.getCause() != null) {
+          return matches(exception.getCause());
+        }
+      }
+      return false;
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/CheckDiscardedFailuresWithIfRulesAndVerticalClassMergingTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/CheckDiscardedFailuresWithIfRulesAndVerticalClassMergingTest.java
index 175b905..d422118 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/CheckDiscardedFailuresWithIfRulesAndVerticalClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/CheckDiscardedFailuresWithIfRulesAndVerticalClassMergingTest.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NoVerticalClassMerging;
@@ -15,7 +16,6 @@
 import com.android.tools.r8.errors.CheckDiscardDiagnostic;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.AssertUtils;
-import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -55,7 +55,13 @@
                     "-if class " + A.class.getTypeName(), "-keep class " + C.class.getTypeName())
                 .addNoVerticalClassMergingAnnotations()
                 .addVerticallyMergedClassesInspector(
-                    VerticallyMergedClassesInspector::assertNoClassesMerged)
+                    inspector -> {
+                      if (enableVerticalClassMerging) {
+                        inspector.assertMergedIntoSubtype(A.class);
+                      } else {
+                        inspector.assertNoClassesMerged();
+                      }
+                    })
                 // Intentionally fail compilation due to -checkdiscard. This triggers the
                 // (re)running of the Enqueuer after the final round of tree shaking, for generating
                 // -whyareyoukeeping output.
@@ -75,7 +81,14 @@
                         diagnostics.assertNoMessages();
                       }
                     })
-                .inspect(inspector -> assertThat(inspector.clazz(C.class), isPresent()))
+                // TODO(b/266049507): It is questionable not to keep C when vertical class merging
+                // is enabled. A simple fix is to disable vertical class merging of classes matched
+                // by the -if condition.
+                .inspect(
+                    inspector ->
+                        assertThat(
+                            inspector.clazz(C.class),
+                            notIf(isPresent(), enableVerticalClassMerging)))
                 .run(parameters.getRuntime(), Main.class)
                 .assertSuccessWithOutputLines("B"));
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
index 3247e60..f28c2a5 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
@@ -6,7 +6,9 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NoAccessModification;
@@ -87,11 +89,6 @@
     public void configure(R8FullTestBuilder builder) {
       super.configure(builder);
       builder
-          .addVerticallyMergedClassesInspector(
-              inspector ->
-                  inspector
-                      .applyIf(enableVerticalClassMerging, i -> i.assertMergedIntoSubtype(A.class))
-                      .assertNoOtherClassesMerged())
           .enableNeverClassInliningAnnotations()
           .enableNoRedundantFieldLoadEliminationAnnotations();
     }
@@ -119,13 +116,16 @@
       assertThat(testClassSubject, isPresent());
 
       if (enableVerticalClassMerging) {
+        // Verify that SuperTestClass has been merged into TestClass.
+        assertThat(inspector.clazz(SuperTestClass.class), not(isPresent()));
+        assertEquals(
+            "java.lang.Object", testClassSubject.getDexProgramClass().superType.toSourceString());
+
         // Verify that TestClass.field has been removed.
         assertEquals(1, testClassSubject.allFields().size());
 
-        // Due to the -if rule, the SuperTestClass is only merged into TestClass after the final
-        // round of tree shaking, at which point TestClass.field has already been removed.
-        // Therefore, we expect no collision to have happened.
-        assertEquals("field", testClassSubject.allFields().get(0).getFinalName());
+        // Verify that there was a naming conflict such that SuperTestClass.field was renamed.
+        assertNotEquals("field", testClassSubject.allFields().get(0).getFinalName());
       }
     }
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
index 1d9d03b..aa46e96 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.NoAccessModification;
 import com.android.tools.r8.NoHorizontalClassMerging;
@@ -86,14 +87,7 @@
     @Override
     public void configure(R8FullTestBuilder builder) {
       super.configure(builder);
-      builder
-          .addVerticallyMergedClassesInspector(
-              inspector ->
-                  inspector
-                      .applyIf(enableVerticalClassMerging, i -> i.assertMergedIntoSubtype(A.class))
-                      .assertNoOtherClassesMerged())
-          .enableInliningAnnotations()
-          .enableNoHorizontalClassMergingAnnotations();
+      builder.enableNoHorizontalClassMergingAnnotations();
     }
 
     @Override
@@ -131,10 +125,8 @@
                 .collect(Collectors.toList());
         assertEquals(1, methods.size());
 
-        // Due to the -if rule, the SuperTestClass is only merged into TestClass after the final
-        // round of tree shaking, at which point TestClass.method has already been removed.
-        // Therefore, we expect no collision to have happened.
-        assertEquals("method", methods.get(0).getFinalName());
+        // Verify that there was a naming conflict such that SuperTestClass.method was renamed.
+        assertNotEquals("method", methods.get(0).getFinalName());
       }
     }
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
index 93c9dec..9e98e16 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
@@ -8,6 +8,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.AssumeMayHaveSideEffects;
 import com.android.tools.r8.NoAccessModification;
@@ -95,15 +96,7 @@
     @Override
     public void configure(R8FullTestBuilder builder) {
       super.configure(builder);
-      builder
-          .addVerticallyMergedClassesInspector(
-              inspector ->
-                  inspector
-                      .applyIf(enableVerticalClassMerging, i -> i.assertMergedIntoSubtype(A.class))
-                      .assertNoOtherClassesMerged())
-          .enableInliningAnnotations()
-          .enableNoHorizontalClassMergingAnnotations()
-          .enableSideEffectAnnotations();
+      builder.enableNoHorizontalClassMergingAnnotations().enableSideEffectAnnotations();
     }
 
     @Override
@@ -141,10 +134,8 @@
                 .collect(Collectors.toList());
         assertEquals(1, methods.size());
 
-        // Due to the -if rule, the SuperTestClass is only merged into TestClass after the final
-        // round of tree shaking, at which point TestClass.method has already been removed.
-        // Therefore, we expect no collision to have happened.
-        assertEquals("method", methods.get(0).getFinalName());
+        // Verify that there was a naming conflict such that SuperTestClass.method was renamed.
+        assertNotEquals("method", methods.get(0).getFinalName());
       }
     }
   }