Use generated covariant methods to synthesize methods on library

Bug: b/234579501
Change-Id: Ia7417001a936633d665c84813f6d45e8749776a2
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index f10a654..717ac38d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -287,6 +287,10 @@
     return Iterables.filter(virtualMethods(), predicate::test);
   }
 
+  public void addVirtualMethod(DexEncodedMethod method) {
+    methodCollection.addVirtualMethod(method);
+  }
+
   public void addVirtualMethods(Collection<DexEncodedMethod> methods) {
     methodCollection.addVirtualMethods(methods);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index b49abbe..5e6c879 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -694,10 +694,6 @@
     methodCollection.addMethod(method);
   }
 
-  public void addVirtualMethod(DexEncodedMethod virtualMethod) {
-    methodCollection.addVirtualMethod(virtualMethod);
-  }
-
   public void replaceVirtualMethod(
       DexMethod virtualMethod, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
     methodCollection.replaceVirtualMethod(virtualMethod, replacement);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index df2c436..0c41415 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -6,7 +6,6 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexItemFactory.EnumMembers;
@@ -14,7 +13,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
-import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
 import com.android.tools.r8.ir.optimize.info.LibraryOptimizationInfoInitializerFeedback;
 import com.android.tools.r8.ir.optimize.info.field.EmptyInstanceFieldInitializationInfoCollection;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
@@ -87,16 +85,6 @@
     }
   }
 
-  private void modelStaticFinalLibraryFields(Set<DexEncodedField> finalLibraryFields) {
-    for (DexEncodedField field : finalLibraryFields) {
-      if (field.isStatic()) {
-        feedback.recordLibraryFieldHasAbstractValue(
-            field,
-            abstractValueFactory.createSingleFieldValue(field.getReference(), ObjectState.empty()));
-      }
-    }
-  }
-
   private void modelLibraryMethodsNonNullParamOrThrow() {
     dexItemFactory.libraryMethodsNonNullParamOrThrow.forEach(
         (method, nonNullParamOrThrow) -> {
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 3addb2d..ec4ef3b 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -16,6 +16,8 @@
 import static java.util.Collections.emptySet;
 
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.androidapi.CovariantReturnTypeMethods;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
@@ -72,6 +74,7 @@
 import com.android.tools.r8.graph.LookupMethodTarget;
 import com.android.tools.r8.graph.LookupResult;
 import com.android.tools.r8.graph.LookupTarget;
+import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodAccessInfoCollection;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
@@ -3546,9 +3549,8 @@
     includeMinimumKeepInfo(rootSet);
 
     if (mode.isInitialTreeShaking()) {
-      // This is simulating the effect of the "root set" applied rules.
-      // This is done only in the initial pass, in subsequent passes the "rules" are reapplied
-      // by iterating the instances.
+      // Amend library methods with covariant return types.
+      modelLibraryMethodsWithCovariantReturnTypes();
     } else if (appView.getKeepInfo() != null) {
       EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
       appView
@@ -3590,6 +3592,29 @@
             this::recordDependentMinimumKeepInfo);
   }
 
+  private void modelLibraryMethodsWithCovariantReturnTypes() {
+    CovariantReturnTypeMethods.registerMethodsWithCovariantReturnType(
+        appView.dexItemFactory(),
+        method -> {
+          DexLibraryClass libraryClass =
+              DexLibraryClass.asLibraryClassOrNull(
+                  appView.appInfo().definitionForWithoutExistenceAssert(method.getHolderType()));
+          if (libraryClass == null) {
+            return;
+          }
+          DexEncodedMethod covariantMethod = libraryClass.lookupMethod(method);
+          if (covariantMethod != null) {
+            return;
+          }
+          libraryClass.addVirtualMethod(
+              DexEncodedMethod.builder()
+                  .setMethod(method)
+                  .setAccessFlags(MethodAccessFlags.builder().setPublic().build())
+                  .setApiLevelForDefinition(ComputedApiLevel.notSet())
+                  .build());
+        });
+  }
+
   private void applyMinimumKeepInfo(DexProgramClass clazz) {
     EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
     KeepClassInfo.Joiner minimumKeepInfoForClass =
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideCovariantTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideCovariantTest.java
index 23febd2..89bad76 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideCovariantTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideCovariantTest.java
@@ -10,8 +10,8 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentHashMap.KeySetView;
 import org.junit.Test;
@@ -57,18 +57,17 @@
         .addLibraryClasses(LibraryUser.class)
         .addKeepMainRule(Main.class)
         .setMinApi(parameters.getApiLevel())
+        .applyIf(
+            parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
+            builder -> builder.addDontWarn(KeySetView.class))
         .enableInliningAnnotations()
         .compile()
         .addRunClasspathFiles(buildOnDexRuntime(parameters, LibraryUser.class))
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatMatchesIf(
-            parameters.isCfRuntime(), containsString("Hello World"))
-        .assertFailureWithErrorThatThrowsIf(
-            !supportsKeySetView() && parameters.isDexRuntime(), NoSuchMethodError.class)
-        // TODO(b/234579501): We fail to keep the library method override.
         .applyIf(
-            parameters.isDexRuntime() && supportsKeySetView(),
-            TestRunResult::assertSuccessWithEmptyOutput);
+            supportsKeySetView(),
+            result -> result.assertFailureWithErrorThatMatches(containsString("Hello World")),
+            result -> result.assertFailureWithErrorThatThrows(NoSuchMethodError.class));
   }
 
   private byte[] getMainWithoutSyntheticBridgeForKeySet() throws Exception {