Add test for trying to compute method states for dynamic null receiver

Bug: b/250634405
Change-Id: I7ca63f38483c1e953b42c8b1737974f5b6897039
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index d4c83b0..eff76c7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -157,7 +157,8 @@
       return;
     }
 
-    if (invoke.isInvokeMethodWithReceiver()
+    if (appView.options().testing.checkReceiverAlwaysNullInCallSiteOptimization
+        && invoke.isInvokeMethodWithReceiver()
         && invoke.asInvokeMethodWithReceiver().getReceiver().isAlwaysNull(appView)) {
       // Nothing to propagate; the invoke instruction always fails.
       return;
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 3b87985..ca9c1e7 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1916,6 +1916,7 @@
 
     public boolean neverReuseCfLocalRegisters = false;
     public boolean roundtripThroughLIR = false;
+    public boolean checkReceiverAlwaysNullInCallSiteOptimization = true;
     private boolean hasReadCheckDeterminism = false;
     private DeterminismChecker determinismChecker = null;
     public boolean usePcEncodingInCfForTesting = false;
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithNullReceiverBoundTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithNullReceiverBoundTest.java
new file mode 100644
index 0000000..5b09e47
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithNullReceiverBoundTest.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2022, 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.optimize.argumentpropagation;
+
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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;
+
+/**
+ * This is an attempt on a regression test for b/250634405. What happens in the input program is
+ * that we determine a phi value to be always null in Phi.getDynamicUpperBoundType. That information
+ * has not been propagated to the receiver during optimizing of the IR. Therefor the check at {@link
+ * ArgumentPropagatorCodeScanner.scan} for receiver always being null returns false.
+ *
+ * <p>Getting the exact IR to match was difficult so this test short circuit this by disabling IR
+ * processing of a simple method (by specifying pass-through) and disabling the check in {@link
+ * ArgumentPropagatorCodeScanner.scan}.
+ */
+@RunWith(Parameterized.class)
+public class PolymorphicMethodWithNullReceiverBoundTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    // TODO(b/250634405): Check for null in dynamic receiver type.
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForR8(parameters.getBackend())
+                .addInnerClasses(getClass())
+                .addKeepMainRule(Main.class)
+                .enableNoHorizontalClassMergingAnnotations()
+                .enableNoVerticalClassMergingAnnotations()
+                .setMinApi(parameters.getApiLevel())
+                .addOptionsModification(
+                    options -> {
+                      options.testing.cfByteCodePassThrough =
+                          method -> method.getName().startsWith("main");
+                      options.testing.checkReceiverAlwaysNullInCallSiteOptimization = false;
+                    })
+                .compile());
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      I i = System.currentTimeMillis() > 0 ? new A() : new B();
+      i = null;
+      i.m();
+    }
+  }
+
+  public interface I {
+
+    ReturnType m();
+  }
+
+  @NoHorizontalClassMerging
+  @NoVerticalClassMerging
+  static class A implements I {
+
+    @Override
+    public ReturnType m() {
+      System.out.println("A.m()");
+      return new ReturnType();
+    }
+  }
+
+  static class B implements I {
+
+    @Override
+    public ReturnType m() {
+      System.out.println("B.m()");
+      return new ReturnType();
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class ReturnType {}
+}