Reproduce and workaround definitionFor failures with checkdiscard errors

Bug: b/266049507
Bug: b/265960138
Change-Id: Idb8dd04d5a7d6831f3f1ae62de110113a9b91d25
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 8596642..f56779b 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -118,9 +118,13 @@
                 // `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.definitionFor(sourceType));
+                    asProgramClassOrNull(
+                        appView.appInfo().definitionForWithoutExistenceAssert(sourceType));
                 if (sourceClass == null) {
-                  assert false;
+                  // 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) {
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 7d8610b..5717ccf 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -1965,6 +1965,9 @@
       if (graphLens.isIdentityLens()) {
         return this;
       }
+      // TODO(b/164019179): If rules can now reference dead items. These should be pruned or
+      //  rewritten
+      ifRules.forEach(ProguardIfRule::canReferenceDeadTypes);
       return new RootSet(
           getDependentMinimumKeepInfo().rewrittenWithLens(graphLens),
           reasonAsked,
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
new file mode 100644
index 0000000..2541213
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/CheckDiscardedFailuresWithIfRulesAndVerticalClassMergingTest.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.ifrule.verticalclassmerging;
+
+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;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.errors.CheckDiscardDiagnostic;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.AssertUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CheckDiscardedFailuresWithIfRulesAndVerticalClassMergingTest extends TestBase {
+
+  @Parameter(0)
+  public boolean enableCheckDiscard;
+
+  @Parameter(1)
+  public boolean enableVerticalClassMerging;
+
+  @Parameter(2)
+  public TestParameters parameters;
+
+  @Parameters(name = "{2}, check discard: {0}, vertical class merging: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(),
+        BooleanUtils.values(),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  @Test
+  public void test() throws Exception {
+    AssertUtils.assertFailsCompilationIf(
+        enableCheckDiscard,
+        () ->
+            testForR8(parameters.getBackend())
+                .addInnerClasses(getClass())
+                .addKeepMainRule(Main.class)
+                .addKeepRules(
+                    "-if class " + A.class.getTypeName(), "-keep class " + C.class.getTypeName())
+                .addNoVerticalClassMergingAnnotations()
+                .addVerticallyMergedClassesInspector(
+                    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.
+                .applyIf(
+                    enableCheckDiscard,
+                    testBuilder ->
+                        testBuilder.addKeepRules("-checkdiscard class " + Main.class.getTypeName()))
+                .applyIf(
+                    !enableVerticalClassMerging,
+                    R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+                .setMinApi(parameters.getApiLevel())
+                .compileWithExpectedDiagnostics(
+                    diagnostics -> {
+                      if (enableCheckDiscard) {
+                        diagnostics.assertErrorsMatch(diagnosticType(CheckDiscardDiagnostic.class));
+                      } else {
+                        diagnostics.assertNoMessages();
+                      }
+                    })
+                // 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"));
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(new B());
+    }
+  }
+
+  @NoVerticalClassMerging
+  static class A {}
+
+  static class B extends A {
+
+    @Override
+    public String toString() {
+      return "B";
+    }
+  }
+
+  static class C {}
+}