Fix and regression test for lambda with undefined interface.
Bug: b/232379893
Bug: b/233868787
Change-Id: I845f13b92121f2ae1eba470246ded606464d5bf6
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 09df4b5..39901f4 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3599,10 +3599,31 @@
}
}
+ BiConsumer<LambdaClass, ProgramMethod> lambdaCallback = this::recordLambdaSynthesizingContext;
+ // TODO(b/233868787): If a lambda implements unknown interfaces its methods won't be live and if
+ // the tree-pruner is disabled they won't be removed. Workaround this by making them live.
+ if (!options.isShrinking()) {
+ lambdaCallback =
+ lambdaCallback.andThen(
+ (clazz, context) -> {
+ for (DexType itf : clazz.getLambdaProgramClass().getInterfaces()) {
+ if (definitionFor(itf, context) == null) {
+ for (ProgramMethod method :
+ clazz.getLambdaProgramClass().virtualProgramMethods()) {
+ synchronized (additions) {
+ additions.addLiveMethod(method);
+ }
+ }
+ break;
+ }
+ }
+ });
+ }
+
R8CfInstructionDesugaringEventConsumer eventConsumer =
CfInstructionDesugaringEventConsumer.createForR8(
appView,
- this::recordLambdaSynthesizingContext,
+ lambdaCallback,
this::recordConstantDynamicSynthesizingContext,
this::recordTwrCloseResourceMethodSynthesizingContext,
additions,
diff --git a/src/test/java/com/android/tools/r8/regress/UndefinedLambdaInterfaceRegress232379893.java b/src/test/java/com/android/tools/r8/regress/UndefinedLambdaInterfaceRegress232379893.java
new file mode 100644
index 0000000..b27e96a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/UndefinedLambdaInterfaceRegress232379893.java
@@ -0,0 +1,62 @@
+// 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.regress;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class UndefinedLambdaInterfaceRegress232379893 extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public UndefinedLambdaInterfaceRegress232379893(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addProgramClassesAndInnerClasses(UserOfUndefinedInterface.class)
+ .addKeepMainRule(TestClass.class)
+ .addDontWarn(UndefinedInterface.class)
+ .addDontShrink()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ interface UndefinedInterface {
+ void foo();
+ }
+
+ static class UserOfUndefinedInterface {
+ public static void bar(UndefinedInterface i) {
+ throw new RuntimeException("unused method");
+ }
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ if (System.nanoTime() < 0) {
+ UserOfUndefinedInterface.bar(() -> System.out.println("unused lambda"));
+ } else {
+ System.out.println("Hello, world");
+ }
+ }
+ }
+}