Fix optimization of Objects.requireNonNullElse

Fixes: 197057070
Change-Id: I5c2a14fd56f13fa88e091a494c106cd082d3a6d8
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
index 4b7cffb..1e518d6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
@@ -177,11 +177,14 @@
       instructionIterator.removeOrReplaceByDebugLocalRead();
     } else if (inValue.isAlwaysNull(appView)) {
       if (singleTarget.getReference() == objectsMethods.requireNonNullElse) {
-        // Optimize Objects.requireNonNullElse(null, defaultObj) into defaultObj.
-        if (invoke.hasOutValue()) {
-          invoke.outValue().replaceUsers(invoke.getLastArgument(), affectedValues);
+        // Optimize Objects.requireNonNullElse(null, defaultObj) into defaultObj if defaultObj
+        // is never null.
+        if (invoke.getLastArgument().isNeverNull()) {
+          if (invoke.hasOutValue()) {
+            invoke.outValue().replaceUsers(invoke.getLastArgument(), affectedValues);
+          }
+          instructionIterator.removeOrReplaceByDebugLocalRead();
         }
-        instructionIterator.removeOrReplaceByDebugLocalRead();
       } else if (singleTarget.getReference() == objectsMethods.requireNonNullElseGet) {
         // Don't optimize Objects.requireNonNullElseGet. The result of calling supplier.get() still
         // needs a null-check, so two invokes will be needed.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseTest.java
index 39acb1e..bde8237 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseTest.java
@@ -36,6 +36,16 @@
   }
 
   @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(getProgramClassFileData())
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Foo", "Bar", "Expected NPE");
+  }
+
+  @Test
   public void test() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramClassFileData(getProgramClassFileData())
@@ -62,7 +72,7 @@
                   testNullArgumentMethodSubject, not(invokesMethodWithName("requireNonNullElse")));
             })
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("Foo", "Bar");
+        .assertSuccessWithOutputLines("Foo", "Bar", "Expected NPE");
   }
 
   private byte[] getProgramClassFileData() throws IOException {
@@ -77,6 +87,12 @@
     public static void main(String[] args) {
       testNonNullArgument();
       testNullArgument();
+      try {
+        testNullArgumentAndNullDefaultValue();
+        System.out.println("Unexpected");
+      } catch (NullPointerException e) {
+        System.out.println("Expected NPE");
+      }
     }
 
     @NeverInline
@@ -88,6 +104,11 @@
     static void testNullArgument() {
       System.out.println(Mock.requireNonNullElse(null, "Bar"));
     }
+
+    @NeverInline
+    static void testNullArgumentAndNullDefaultValue() {
+      System.out.println(Mock.requireNonNullElse(null, null));
+    }
   }
 
   // References to this class are rewritten to java.util.Objects by transformation.