Fix inadequate call site propagation in presence of lambda targets

Bug: 153147271, 150269949
Change-Id: Ief75107ee14a90d4da32c4c5d568bfdf0997f1e4
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 dc319a8..951cb63 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
@@ -3,19 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.cf.TypeVerificationHelper;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
-import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
 import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -24,7 +25,8 @@
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import java.util.BitSet;
 import java.util.Collection;
@@ -77,14 +79,21 @@
 
   // TODO(b/140204899): Refactor lookup methods to be defined in a single place.
   public Collection<DexEncodedMethod> lookupTargets(
-      AppView<? extends AppInfoWithSubtyping> appView, DexType invocationContext) {
-    // Leverage exact receiver type if available.
-    DexEncodedMethod singleTarget = lookupSingleTarget(appView, invocationContext);
-    if (singleTarget != null) {
-      return ImmutableList.of(singleTarget);
+      AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
+    if (!isInvokeMethodWithDynamicDispatch()) {
+      DexEncodedMethod singleTarget = lookupSingleTarget(appView, invocationContext);
+      return singleTarget != null ? ImmutableSet.of(singleTarget) : null;
     }
-    if (!isInvokeMethodWithDynamicDispatch() || !appView.appInfo().hasLiveness()) {
-      return null;
+    DexProgramClass refinedReceiverUpperBound =
+        asProgramClassOrNull(
+            appView.definitionFor(
+                TypeAnalysis.getRefinedReceiverType(appView, asInvokeMethodWithReceiver())));
+    DexProgramClass refinedReceiverLowerBound = null;
+    ClassTypeElement refinedReceiverLowerBoundType =
+        asInvokeMethodWithReceiver().getReceiver().getDynamicLowerBoundType(appView);
+    if (refinedReceiverLowerBoundType != null) {
+      refinedReceiverLowerBound =
+          asProgramClassOrNull(appView.definitionFor(refinedReceiverLowerBoundType.getClassType()));
     }
     LookupResultSuccess lookupResult =
         appView
@@ -92,48 +101,18 @@
             .resolveMethod(method.holder, method)
             .lookupVirtualDispatchTargets(
                 appView.definitionForProgramType(invocationContext),
-                appView.withLiveness().appInfo())
+                appView.withLiveness().appInfo(),
+                refinedReceiverUpperBound,
+                refinedReceiverLowerBound)
             .asLookupResultSuccess();
-    if (lookupResult == null || lookupResult.isEmpty()) {
+    if (lookupResult == null) {
       return null;
     }
-    if (lookupResult.hasLambdaTargets()) {
-      // TODO(b/150277553): Support lambda targets.
-      return null;
-    }
-    assert lookupResult.hasMethodTargets();
-    DexType staticReceiverType = getInvokedMethod().holder;
-    DexType refinedReceiverType =
-        TypeAnalysis.getRefinedReceiverType(
-            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();
-        lookupResult.forEach(
-            methodTarget -> {
-              DexEncodedMethod target = methodTarget.getMethod();
-              if (target == refinedTarget
-                  || appView.isSubtype(target.holder(), refinedReceiverType).isPossiblyTrue()) {
-                result.add(target);
-              }
-            },
-            lambdaTarget -> {
-              throw new Unreachable();
-            });
-        return result;
-      }
-      // If resolution at the refined type fails, conservatively return the full set of targets.
-    }
     Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
     lookupResult.forEach(
         methodTarget -> result.add(methodTarget.getMethod()),
         lambda -> {
-          assert false;
+          // TODO(b/150277553): Support lambda targets.
         });
     return result;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index f7b8688..41b1124 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.code.InvokePolymorphicRange;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -22,6 +21,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Collection;
 import java.util.List;
 
@@ -126,7 +126,7 @@
 
   @Override
   public Collection<DexEncodedMethod> lookupTargets(
-      AppView<? extends AppInfoWithSubtyping> appView, DexType invocationContext) {
+      AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
     // TODO(herhut): Implement lookup target for invokePolymorphic.
     return null;
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java
new file mode 100644
index 0000000..c04f9b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java
@@ -0,0 +1,69 @@
+// 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.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 CallSiteOptimizationWithLambdaTargetTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public CallSiteOptimizationWithLambdaTargetTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(CallSiteOptimizationWithLambdaTargetTest.class)
+        .addKeepMainRule(TestClass.class)
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("true", "false");
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      new A().m(new Object());
+      get().m(null);
+    }
+
+    static I get() {
+      return System.currentTimeMillis() >= 0 ? new A() : System.out::println;
+    }
+  }
+
+  interface I {
+
+    void m(Object o);
+  }
+
+  @NeverClassInline
+  static class A implements I {
+
+    @NeverInline
+    @Override
+    public void m(Object o) {
+      System.out.println(o != null);
+    }
+  }
+}