Soft-pin types referenced from unresolvable methods

Bug: b/236875523
Change-Id: I1d862f719172b1be6070308d101b7dca955a34ce
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 6214a5a..40a566f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3436,6 +3436,20 @@
             markMethodAsTargeted(new ProgramMethod(clazz, method), reason);
           }
         });
+
+    // Disallow optimization of types referenced from unresolvable methods. The graph lenses created
+    // by various optimizations only store mappings for method definitions, thus no lenses contain
+    // mappings for unresolvable methods. This can be problematic if an unresolvable method refers
+    // to a class that no longer exists as a result of an optimization.
+    for (DexType referencedType : symbolicMethod.getReferencedBaseTypes(appView.dexItemFactory())) {
+      if (referencedType.isClassType()) {
+        DexProgramClass clazz = asProgramClassOrNull(definitionFor(referencedType, context));
+        if (clazz != null) {
+          applyMinimumKeepInfoWhenLive(
+              clazz, KeepClassInfo.newEmptyJoiner().disallowOptimization());
+        }
+      }
+    }
   }
 
   private DexMethod generatedEnumValuesMethod(DexClass enumClass) {
@@ -3576,7 +3590,7 @@
           .forEachRuleInstance(
               appView,
               (clazz, minimumKeepInfo) ->
-                  applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfo),
+                  applyMinimumKeepInfoWhenLive(clazz, minimumKeepInfo, preconditionEvent),
               (field, minimumKeepInfo) ->
                   applyMinimumKeepInfoWhenLive(field, minimumKeepInfo, preconditionEvent),
               this::applyMinimumKeepInfoWhenLiveOrTargeted);
@@ -3646,8 +3660,14 @@
 
   private void applyMinimumKeepInfoWhenLive(
       DexProgramClass clazz,
-      EnqueuerEvent preconditionEvent,
       KeepClassInfo.Joiner minimumKeepInfo) {
+    applyMinimumKeepInfoWhenLive(clazz, minimumKeepInfo, EnqueuerEvent.unconditional());
+  }
+
+  private void applyMinimumKeepInfoWhenLive(
+      DexProgramClass clazz,
+      KeepClassInfo.Joiner minimumKeepInfo,
+      EnqueuerEvent preconditionEvent) {
     if (liveTypes.contains(clazz)) {
       keepInfo.joinClass(clazz, info -> info.merge(minimumKeepInfo));
     } else {
@@ -3674,7 +3694,7 @@
       DexProgramClass clazz,
       KeepClassInfo.Joiner minimumKeepInfo) {
     if (isPreconditionForMinimumKeepInfoSatisfied(preconditionEvent)) {
-      applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfo);
+      applyMinimumKeepInfoWhenLive(clazz, minimumKeepInfo, preconditionEvent);
     } else {
       dependentMinimumKeepInfo
           .getOrCreateMinimumKeepInfoFor(preconditionEvent)
@@ -3803,7 +3823,7 @@
       minimumKeepClassInfoDependentOnPrecondition.forEach(
           appView,
           (clazz, minimumKeepInfoForClass) ->
-              applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfoForClass),
+              applyMinimumKeepInfoWhenLive(clazz, minimumKeepInfoForClass, preconditionEvent),
           (field, minimumKeepInfoForField) ->
               applyMinimumKeepInfoWhenLive(field, minimumKeepInfoForField, preconditionEvent),
           (method, minimumKeepInfoForMethod) ->
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/UnresolvableMethodWithClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/UnresolvableMethodWithClassMergingTest.java
index 4a027fc..bfa57f9 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/UnresolvableMethodWithClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/UnresolvableMethodWithClassMergingTest.java
@@ -4,8 +4,8 @@
 
 package com.android.tools.r8.classmerging.horizontal;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
 import org.junit.Test;
 
 public class UnresolvableMethodWithClassMergingTest extends HorizontalClassMergingTestBase {
@@ -14,15 +14,15 @@
     super(parameters);
   }
 
-  // TODO(christofferqa): Should not fail compilation.
-  @Test(expected = CompilationFailedException.class)
+  @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramClasses(Main.class, A.class, B.class)
         .addKeepMainRule(Main.class)
         .addDontWarn(Missing.class)
         .addHorizontallyMergedClassesInspector(
-            inspector -> inspector.assertMergedInto(B.class, A.class).assertNoOtherClassesMerged())
+            HorizontallyMergedClassesInspector::assertNoOtherClassesMerged)
+        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .addRunClasspathFiles(buildOnDexRuntime(parameters, Missing.class))