Version 1.5.45

Cherry pick: Mark bridge methods on interfaces as live
CL: https://r8-review.googlesource.com/c/r8/+/39306

Cherry pick: Do not mark library methods as reachable
CL: https://r8-review.googlesource.com/c/r8/+/38300

Bug: 133457361
Change-Id: Ica076a9cfa137a92a1e2d1cfe1df0c59e616c8af
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 1041a82..92e7a74 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 = "1.5.44";
+  public static final String LABEL = "1.5.45";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index a9773f8..6122236 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -363,15 +363,31 @@
       }
     }
 
+    Consumer<DexEncodedMethod> addIfNotAbstract =
+        m -> {
+          if (!m.accessFlags.isAbstract()) {
+            result.add(m);
+          }
+        };
+    // Default methods are looked up when looking at a specific subtype that does not override them.
+    // Otherwise, we would look up default methods that are actually never used. However, we have to
+    // add bridge methods, otherwise we can remove a bridge that will be used.
+    Consumer<DexEncodedMethod> addIfNotAbstractAndBridge =
+        m -> {
+          if (!m.accessFlags.isAbstract() && m.accessFlags.isBridge()) {
+            result.add(m);
+          }
+        };
+
     Set<DexType> set = subtypes(method.holder);
     for (DexType type : set) {
       DexClass clazz = definitionFor(type);
-      // Default methods are looked up when looking at a specific subtype that does not
-      // override them, so we ignore interfaces here. Otherwise, we would look up default methods
-      // that are factually never used.
-      if (!clazz.isInterface()) {
+      if (clazz.isInterface()) {
+        ResolutionResult targetMethods = resolveMethodOnInterface(type, method);
+        targetMethods.forEachTarget(addIfNotAbstractAndBridge);
+      } else {
         ResolutionResult targetMethods = resolveMethodOnClass(type, method);
-        targetMethods.forEachTarget(result::add);
+        targetMethods.forEachTarget(addIfNotAbstract);
       }
     }
     return result;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 9f48d56..d02b735 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
 import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
 import static com.android.tools.r8.shaking.EnqueuerUtils.extractProgramFieldDefinitions;
+import static com.google.common.base.Predicates.or;
 
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.dex.IndexedItemCollection;
@@ -1298,7 +1299,9 @@
 
   private boolean isInstantiatedOrHasInstantiatedSubtype(DexType type) {
     return instantiatedTypes.contains(type)
-        || appInfo.subtypes(type).stream().anyMatch(instantiatedTypes::contains);
+        || instantiatedLambdas.contains(type)
+        || appInfo.subtypes(type).stream()
+            .anyMatch(or(instantiatedTypes::contains, instantiatedLambdas::contains));
   }
 
   private void markInstanceFieldAsReachable(DexField field, KeepReason reason) {
@@ -1377,42 +1380,61 @@
         interfaceInvoke
             ? appInfo.lookupInterfaceTargets(method)
             : appInfo.lookupVirtualTargets(method);
-    for (DexEncodedMethod encodedMethod : possibleTargets) {
+    for (DexEncodedMethod encodedPossibleTarget : possibleTargets) {
+      DexMethod possibleTarget = encodedPossibleTarget.method;
+      DexClass clazz = appView.definitionFor(possibleTarget.holder);
+      if (clazz == null) {
+        assert false;
+        continue;
+      }
+
+      if (!clazz.isProgramClass()) {
+        // Should only be tracing the program.
+        continue;
+      }
+
       // TODO(b/120959039): The reachable.add test might be hiding other paths to the method.
       SetWithReason<DexEncodedMethod> reachable =
           reachableVirtualMethods.computeIfAbsent(
-              encodedMethod.method.holder, ignore -> newSetWithoutReasonReporter());
-      if (reachable.add(encodedMethod, reason)) {
-        // Abstract methods cannot be live.
-        if (!encodedMethod.accessFlags.isAbstract()) {
-          // If the holder type is instantiated, the method is live. Otherwise check whether we find
-          // a subtype that does not shadow this methods but is instantiated.
-          // Note that library classes are always considered instantiated, as we do not know where
-          // they are instantiated.
-          if (isInstantiatedOrHasInstantiatedSubtype(encodedMethod.method.holder)) {
-            if (instantiatedTypes.contains(encodedMethod.method.holder)) {
-              markVirtualMethodAsLive(encodedMethod,
-                  KeepReason.reachableFromLiveType(encodedMethod.method.holder));
-            } else {
-              Deque<DexType> worklist = new ArrayDeque<>();
-              fillWorkList(worklist, encodedMethod.method.holder);
-              while (!worklist.isEmpty()) {
-                DexType current = worklist.pollFirst();
-                DexClass currentHolder = appView.definitionFor(current);
-                // If this class overrides the virtual, abort the search. Note that, according to
-                // the JVM spec, private methods cannot override a virtual method.
-                if (currentHolder == null
-                    || currentHolder.lookupVirtualMethod(encodedMethod.method) != null) {
-                  continue;
-                }
-                if (instantiatedTypes.contains(current)) {
-                  markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(current));
-                  break;
-                }
-                fillWorkList(worklist, current);
-              }
-            }
+              possibleTarget.holder, ignore -> newSetWithoutReasonReporter());
+      if (!reachable.add(encodedPossibleTarget, reason)) {
+        continue;
+      }
+
+      // Abstract methods cannot be live.
+      if (encodedPossibleTarget.accessFlags.isAbstract()) {
+        continue;
+      }
+
+      // If the holder type is instantiated, the method is live. Otherwise check whether we find
+      // a subtype that does not shadow this methods but is instantiated.
+      // Note that library classes are always considered instantiated, as we do not know where
+      // they are instantiated.
+      if (!isInstantiatedOrHasInstantiatedSubtype(possibleTarget.holder)) {
+        continue;
+      }
+
+      if (instantiatedTypes.contains(possibleTarget.holder)
+          || instantiatedLambdas.contains(possibleTarget.holder)) {
+        markVirtualMethodAsLive(
+            encodedPossibleTarget, KeepReason.reachableFromLiveType(possibleTarget.holder));
+      } else {
+        Deque<DexType> worklist =
+            new ArrayDeque<>(appInfo.allImmediateSubtypes(possibleTarget.holder));
+        while (!worklist.isEmpty()) {
+          DexType current = worklist.pollFirst();
+          DexClass currentHolder = appView.definitionFor(current);
+          // If this class overrides the virtual, abort the search. Note that, according to
+          // the JVM spec, private methods cannot override a virtual method.
+          if (currentHolder == null || currentHolder.lookupVirtualMethod(possibleTarget) != null) {
+            continue;
           }
+          if (instantiatedTypes.contains(current)) {
+            markVirtualMethodAsLive(
+                encodedPossibleTarget, KeepReason.reachableFromLiveType(current));
+            break;
+          }
+          appInfo.allImmediateSubtypes(current).forEach(worklist::addLast);
         }
       }
     }
@@ -1444,18 +1466,6 @@
     }
   }
 
-  private void fillWorkList(Deque<DexType> worklist, DexType type) {
-    if (appInfo.isMarkedAsInterface(type)) {
-      // We need to check if the method is shadowed by a class that directly implements
-      // the interface and go recursively down to the sub interfaces to reach class
-      // implementing the interface
-      appInfo.forAllImplementsSubtypes(type, worklist::addLast);
-      appInfo.forAllExtendsSubtypes(type, worklist::addLast);
-    } else {
-      appInfo.forAllExtendsSubtypes(type, worklist::addLast);
-    }
-  }
-
   private void markSuperMethodAsReachable(DexMethod method, DexEncodedMethod from) {
     // We have to mark the immediate target of the descriptor as targeted, as otherwise
     // the invoke super will fail in the resolution step with a NSM error.
diff --git a/src/test/java/com/android/tools/r8/shaking/interfacebridge/LambdaAbstractMethodErrorTest.java b/src/test/java/com/android/tools/r8/shaking/interfacebridge/LambdaAbstractMethodErrorTest.java
new file mode 100644
index 0000000..48576f5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/interfacebridge/LambdaAbstractMethodErrorTest.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2019, 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.interfacebridge;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+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 LambdaAbstractMethodErrorTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().build();
+  }
+
+  public LambdaAbstractMethodErrorTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test_b133457361() throws ExecutionException, CompilationFailedException, IOException {
+    testForR8(parameters.getBackend())
+        .addProgramClassesAndInnerClasses(Main.class)
+        .addProgramClassesAndInnerClasses(Task.class, OuterClass.class)
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            internalOptions -> {
+              internalOptions.enableInlining = false;
+              internalOptions.enableClassInlining = false;
+              internalOptions.enableVerticalClassMerging = false;
+            })
+        .setMinApi(parameters.getRuntime())
+        .run(Main.class.getTypeName())
+        .assertSuccessWithOutput("FOO");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/interfacebridge/Main.java b/src/test/java/com/android/tools/r8/shaking/interfacebridge/Main.java
new file mode 100644
index 0000000..5c3fb27
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/interfacebridge/Main.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2019, 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.interfacebridge;
+
+public class Main {
+
+  public static void main(String[] args) {
+    OuterClass.startTask(result -> System.out.print(result));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/interfacebridge/OuterClass.java b/src/test/java/com/android/tools/r8/shaking/interfacebridge/OuterClass.java
new file mode 100644
index 0000000..af0b8bc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/interfacebridge/OuterClass.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, 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.interfacebridge;
+
+public class OuterClass {
+
+  public interface Callback extends Task.OnTaskFinishedListener<String> {
+    @Override
+    void onTaskFinished(String result);
+  }
+
+  private static class ConcreteTask extends Task<String> {
+
+    public ConcreteTask(Callback listener) {
+      super(listener);
+    }
+
+    @Override
+    public String doInBackground() {
+      return "FOO";
+    }
+  }
+
+  public static void startTask(Callback callback) {
+    new ConcreteTask(callback).execute();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/interfacebridge/Task.java b/src/test/java/com/android/tools/r8/shaking/interfacebridge/Task.java
new file mode 100644
index 0000000..98204ef
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/interfacebridge/Task.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2019, 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.interfacebridge;
+
+public abstract class Task<Result> {
+
+  public interface OnTaskFinishedListener<Result> {
+    void onTaskFinished(Result result);
+  }
+
+  public abstract Result doInBackground();
+
+  public void execute() {
+    onPostExecute(doInBackground());
+  }
+
+  OnTaskFinishedListener<Result> mListener;
+
+  Task(OnTaskFinishedListener<Result> listener) {
+    mListener = listener;
+  }
+
+  public void onPostExecute(Result result) {
+    if (mListener != null) {
+      mListener.onTaskFinished(result);
+    }
+  }
+}