Version 2.0.71

Cherry pick: Reproduce missing propagation in call site optimization
CL: https://r8-review.googlesource.com/c/r8/+/50700

In addition to cherry picking the test case from the CL above, this version includes a fix to InvokeMethod.lookupTargets(), which would incorrectly filter out possible dispatch targets, causing a bug in the call site optimization.

Bug: 154531810
Change-Id: Id4af6e1d2165ea48d010a7b14c3fee6739aa59a8
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index a6e1d74..0bb2d98 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "2.0.70";
+  public static final String LABEL = "2.0.71";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index e7d2ab1..4ade5fd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.cf.TypeVerificationHelper;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -107,23 +108,31 @@
             appView.withLiveness(), this.asInvokeMethodWithReceiver());
     // TODO(b/140204899): Instead of reprocessing here, pass refined receiver to lookup.
     // Leverage refined receiver type if available.
-    if (refinedReceiverType != staticReceiverType) {
-      ResolutionResult refinedResolution =
-          appView.appInfo().resolveMethod(refinedReceiverType, method);
-      if (refinedResolution.isSingleResolution()) {
-        DexEncodedMethod refinedTarget = refinedResolution.getSingleTarget();
-        Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
-        for (DexEncodedMethod target : targets) {
-          if (target == refinedTarget
-              || appView.isSubtype(target.method.holder, refinedReceiverType).isPossiblyTrue()) {
-            result.add(target);
-          }
-        }
-        return result;
-      }
-      // If resolution at the refined type fails, conservatively return the full set of targets.
+    if (refinedReceiverType == staticReceiverType) {
+      return targets;
     }
-    return targets;
+
+    DexClass refinedReceiverClass = appView.definitionFor(refinedReceiverType);
+    if (refinedReceiverClass == null || refinedReceiverClass.isInterface()) {
+      return targets;
+    }
+
+    ResolutionResult refinedResolution =
+        appView.appInfo().resolveMethod(refinedReceiverType, method);
+    if (!refinedResolution.isSingleResolution()) {
+      // If resolution at the refined type fails, conservatively return the full set of targets.
+      return targets;
+    }
+
+    DexEncodedMethod refinedTarget = refinedResolution.getSingleTarget();
+    Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
+    for (DexEncodedMethod target : targets) {
+      if (target == refinedTarget
+          || appView.isSubtype(target.method.holder, refinedReceiverType).isPossiblyTrue()) {
+        result.add(target);
+      }
+    }
+    return result;
   }
 
   public abstract InlineAction computeInlining(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java
new file mode 100644
index 0000000..fb3bc2e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2020, 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.optimize.callsites;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+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.Parameters;
+
+@RunWith(Parameterized.class)
+public class PropagationFromSiblingTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public PropagationFromSiblingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(PropagationFromSiblingTest.class)
+        .addKeepMainRule(TestClass.class)
+        .addOptionsModification(options -> options.enableUnusedInterfaceRemoval = false)
+        .enableInliningAnnotations()
+        .enableMergeAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      getJAsI().m(null); // The null argument is not propagated to A.m() in 2.0.59.
+      new B().m("Hello world!");
+    }
+
+    @NeverInline
+    static I getJAsI() {
+      if (System.currentTimeMillis() > 0) {
+        return new B();
+      }
+      return new C();
+    }
+  }
+
+  interface I {
+    void m(Object o);
+  }
+
+  interface J extends I {}
+
+  @NeverMerge
+  static class A implements I {
+
+    @NeverInline
+    @Override
+    public void m(Object o) {
+      if (o != null) {
+        System.out.println(o.toString());
+      }
+    }
+  }
+
+  @NeverClassInline
+  static class B extends A implements J {}
+
+  static class C implements J {
+
+    @Override
+    public void m(Object o) {}
+  }
+}