Error when methods with @CovariantReturnType are not kept
Bug: b/211362069
Change-Id: Ifbca5553a4d09b4fc62cac13b5c2cb3a9f10086c
diff --git a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
index ea193a5..6bc1e23 100644
--- a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
@@ -119,9 +119,6 @@
CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer,
ExecutorService executorService)
throws ExecutionException {
- if (methodsToProcess.isEmpty()) {
- return;
- }
ThreadUtils.processMap(
methodsToProcess,
(clazz, methods) -> {
diff --git a/src/main/java/com/android/tools/r8/errors/NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic.java b/src/main/java/com/android/tools/r8/errors/NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic.java
new file mode 100644
index 0000000..a737aa4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2024, 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+
+@KeepForApi
+public class NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic implements Diagnostic {
+
+ private final Origin origin;
+ private final MethodReference method;
+ private final MethodPosition position;
+
+ public NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic(ProgramMethod method) {
+ this.origin = method.getOrigin();
+ this.method = method.getMethodReference();
+ this.position = MethodPosition.create(method);
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return position;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return "Methods with @CovariantReturnType annotations should be kept, but was not: "
+ + MethodReferenceUtils.toSourceString(method);
+ }
+}
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 bf8be36..8850e03 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -26,7 +26,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.code.CfOrDexInstruction;
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
-import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.features.IsolatedFeatureSplitsChecker;
@@ -4475,19 +4475,26 @@
}
private void processCovariantReturnTypeAnnotations() throws ExecutionException {
+ if (pendingCovariantReturnTypeDesugaring.isEmpty()) {
+ return;
+ }
+ ProgramMethodMap<Diagnostic> errors = ProgramMethodMap.createConcurrent();
covariantReturnTypeAnnotationTransformer.processMethods(
pendingCovariantReturnTypeDesugaring,
(bridge, target) -> {
- KeepMethodInfo.Joiner bridgeKeepInfo = getKeepInfoForCovariantReturnTypeBridge(target);
+ KeepMethodInfo.Joiner bridgeKeepInfo =
+ getKeepInfoForCovariantReturnTypeBridge(target, errors);
keepInfo.registerCompilerSynthesizedMethod(bridge);
applyMinimumKeepInfoWhenLiveOrTargeted(bridge, bridgeKeepInfo);
profileCollectionAdditions.addMethodIfContextIsInProfile(bridge, target);
},
executorService);
+ errors.forEachValue(appView.reporter()::error);
pendingCovariantReturnTypeDesugaring.clear();
}
- private KeepMethodInfo.Joiner getKeepInfoForCovariantReturnTypeBridge(ProgramMethod target) {
+ private KeepMethodInfo.Joiner getKeepInfoForCovariantReturnTypeBridge(
+ ProgramMethod target, ProgramMethodMap<Diagnostic> errors) {
KeepInfo.Joiner<?, ?, ?> targetKeepInfo =
appView
.rootSet()
@@ -4500,8 +4507,7 @@
if ((options.isMinifying() && targetKeepInfo.isMinificationAllowed())
|| (options.isOptimizing() && targetKeepInfo.isOptimizationAllowed())
|| (options.isShrinking() && targetKeepInfo.isShrinkingAllowed())) {
- // TODO(b/211362069): Report a fatal diagnostic explaining the problem.
- throw new Unimplemented();
+ errors.computeIfAbsent(target, NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic::new);
}
return targetKeepInfo.asMethodJoiner();
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerR8Test.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerR8Test.java
index e8ff92c..37f133f 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerR8Test.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerR8Test.java
@@ -3,18 +3,33 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar.annotations;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static com.android.tools.r8.utils.codeinspector.AssertUtils.assertFailsCompilation;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder.DiagnosticsConsumer;
+import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic;
import com.android.tools.r8.ir.desugar.annotations.CovariantReturnType.CovariantReturnTypes;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import java.util.List;
import java.util.Map;
+import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -75,7 +90,8 @@
compileWithR8(
testBuilder ->
testBuilder.addKeepRules(
- "-keep,allowobfuscation public class * { public <methods>; }")));
+ "-keep,allowobfuscation public class * { public <methods>; }"),
+ this::inspectDiagnostics));
}
@Test
@@ -85,7 +101,8 @@
compileWithR8(
testBuilder ->
testBuilder.addKeepRules(
- "-keep,allowoptimization public class * { public <methods>; }")));
+ "-keep,allowoptimization public class * { public <methods>; }"),
+ this::inspectDiagnostics));
}
@Test
@@ -96,11 +113,19 @@
testBuilder ->
testBuilder.addKeepRules(
"-if public class * -keep class <1> { public <methods>; }",
- "-keep public class *")));
+ "-keep public class *"),
+ this::inspectDiagnostics));
}
private R8TestCompileResult compileWithR8(
ThrowableConsumer<? super R8FullTestBuilder> configuration) throws Exception {
+ return compileWithR8(configuration, TestDiagnosticMessages::assertNoMessages);
+ }
+
+ private R8TestCompileResult compileWithR8(
+ ThrowableConsumer<? super R8FullTestBuilder> configuration,
+ DiagnosticsConsumer<?> diagnosticsConsumer)
+ throws Exception {
return testForR8(parameters.getBackend())
.addProgramClasses(A.class, D.class)
.addProgramClassFileData(
@@ -138,7 +163,29 @@
.addOptionsModification(options -> options.processCovariantReturnTypeAnnotations = true)
.apply(configuration)
.setMinApi(parameters)
- .compile();
+ .compileWithExpectedDiagnostics(diagnosticsConsumer);
+ }
+
+ private void inspectDiagnostics(TestDiagnosticMessages diagnostics) throws Exception {
+ List<MethodReference> methods =
+ ImmutableList.of(
+ Reference.methodFromMethod(B.class.getDeclaredMethod("method")),
+ Reference.methodFromMethod(C.class.getDeclaredMethod("method")),
+ Reference.methodFromMethod(F.class.getDeclaredMethod("method")));
+ List<String> messages =
+ ListUtils.map(
+ methods,
+ method ->
+ "Methods with @CovariantReturnType annotations should be kept, but was not: "
+ + MethodReferenceUtils.toSourceString(method));
+ List<Matcher<Diagnostic>> matchers =
+ ListUtils.map(
+ messages,
+ message ->
+ allOf(
+ diagnosticType(NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic.class),
+ diagnosticMessage(equalTo(message))));
+ diagnostics.assertErrorsMatch(matchers);
}
private void testOnRuntime(R8TestCompileResult r8CompileResult) throws Exception {