Allow for minification of classes with references to missing classes

Bug: 150589374
Bug: 194427614
Change-Id: I898603ecefe6d94ee3171fdb8943e6e18989479a
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index 62b62b9..03627d7 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -66,6 +66,10 @@
     return false;
   }
 
+  public boolean isFailedResolution() {
+    return false;
+  }
+
   public DexClass getInitialResolutionHolder() {
     return null;
   }
@@ -163,6 +167,11 @@
     public boolean isFailedOrUnknownResolution() {
       return true;
     }
+
+    @Override
+    public boolean isFailedResolution() {
+      return true;
+    }
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index 27247d4..e81bd8b 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -116,11 +116,23 @@
       MemberResolutionResult<?, ?> resolutionResult, boolean isInvoke) {
     if (!resolutionResult.isSuccessfulMemberResolutionResult()) {
       // To preserve errors in the original program, we need to look at the failure dependencies.
-      // For example, if this method accesses in a package-private method in another package, and we
-      // move the two methods to the same package, then the invoke would no longer fail with an
+      // For example, if this member accesses a package-private method in another package, and we
+      // move the two members to the same package, then the invoke would no longer fail with an
       // IllegalAccessError.
-      if (isInvoke) {
-        // TODO(b/150589374): Only add this if we are in the minification mode of repackaging.
+
+      // For fields and methods that cannot be found the chance of recovering by repackaging is
+      // pretty slim thus we allow for repackaging the callers.
+      boolean isMissing =
+          (resolutionResult.isMethodResolutionResult()
+                  && resolutionResult
+                      .asMethodResolutionResult()
+                      .isNoSuchMethodErrorResult(context.getContextClass(), appInfo))
+              || (resolutionResult.isFieldResolutionResult()
+                  && resolutionResult.asFieldResolutionResult().isFailedOrUnknownResolution());
+      assert !isMissing
+          || resolutionResult.isMethodResolutionResult()
+          || !resolutionResult.asFieldResolutionResult().isFailedResolution();
+      if (!isMissing) {
         node.addNeighbor(missingTypeNode);
       }
       return;
@@ -161,9 +173,6 @@
     DexClass clazz = appInfo.definitionFor(type);
     if (clazz != null) {
       consumer.accept(clazz);
-    } else {
-      // The missing type reference can be package private and we cannot repackage.
-      node.addNeighbor(missingTypeNode);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index aef58b7..be31b16 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -986,11 +986,6 @@
     if (!keepInfo.getInfo(clazz).isRepackagingAllowed(clazz, options())) {
       return false;
     }
-    for (DexType superType : clazz.allImmediateSupertypes()) {
-      if (definitionFor(superType) == null) {
-        return false;
-      }
-    }
     return clazz
         .traverseProgramMembers(
             member -> {
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index 23e2e5c..90d4f75 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -145,6 +145,14 @@
     return assertFailure();
   }
 
+  public RR assertFailureWithErrorThatMatchesIf(boolean condition, Matcher<String> matcher) {
+    if (condition) {
+      assertStderrMatches(matcher);
+      return assertFailure();
+    }
+    return self();
+  }
+
   public RR assertFailureWithOutput(String expected) {
     assertStdoutMatches(is(expected));
     return assertFailure();
diff --git a/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java
index bdcfd8a..59853ee 100644
--- a/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar;
 
+import static org.hamcrest.CoreMatchers.containsString;
+
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -38,7 +40,12 @@
         .compile()
         .addRunClasspathClasses(MissingInterface.class)
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatThrows(AbstractMethodError.class);
+        // We allow for renaming if the class is missing
+        .assertFailureWithErrorThatMatchesIf(
+            parameters.getDexRuntimeVersion().isDalvik(),
+            containsString(descriptor(MissingInterface.class) + "' is not accessible"))
+        .assertFailureWithErrorThatThrowsIf(
+            !parameters.getDexRuntimeVersion().isDalvik(), IllegalAccessError.class);
   }
 
   interface MissingInterface {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
index df0bfa6..2265ba0 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.repackage;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverInline;
@@ -52,10 +52,6 @@
         .inspect(
             inspector -> {
               // Find the generated lambda class
-              if (!parameters.isDexRuntime()) {
-                assertThat(ClassWithLambda.class, isNotRepackaged(inspector));
-                return;
-              }
               if (repackage) {
                 assertThat(ClassWithLambda.class, isRepackaged(inspector));
               } else {
@@ -66,7 +62,8 @@
                   clazz -> {
                     if (clazz.isSynthesizedJavaLambdaClass()) {
                       assertThat(
-                          clazz.getFinalName(), containsString(Main.class.getPackage().getName()));
+                          clazz.getFinalName(),
+                          startsWith(repackage ? getRepackagePackage() : "a."));
                     }
                   });
             })
@@ -74,7 +71,7 @@
         .run(parameters.getRuntime(), Main.class);
   }
 
-  interface MissingInterface {
+  public interface MissingInterface {
 
     void bar(int x);
   }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingMemberReferenceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingMemberReferenceTest.java
index 9dc8746..cfa6e9a 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageMissingMemberReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingMemberReferenceTest.java
@@ -7,7 +7,6 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -25,16 +24,16 @@
 
   @Test
   public void testR8WithoutRepackaging() throws Exception {
-    runTest(false).assertSuccessWithOutputLines(EXPECTED);
+    runTest(false);
   }
 
   @Test
   public void testR8() throws Exception {
-    runTest(true).assertSuccessWithOutputLines(EXPECTED);
+    runTest(true);
   }
 
-  private R8TestRunResult runTest(boolean repackage) throws Exception {
-    return testForR8(parameters.getBackend())
+  private void runTest(boolean repackage) throws Exception {
+    testForR8(parameters.getBackend())
         .addProgramClasses(ClassWithMissingReferenceInCode.class, Main.class)
         .addKeepMainRule(Main.class)
         .applyIf(repackage, this::configureRepackaging)
@@ -44,12 +43,14 @@
         .compile()
         .inspect(
             inspector ->
-                assertThat(ClassWithMissingReferenceInCode.class, isNotRepackaged(inspector)))
+                assertThat(
+                    ClassWithMissingReferenceInCode.class, isRepackagedIf(inspector, repackage)))
         .addRunClasspathClasses(MissingReference.class)
-        .run(parameters.getRuntime(), Main.class);
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
   }
 
-  static class MissingReference {
+  public static class MissingReference {
     public static void doSomething() {
       System.out.println("MissingReference::doSomething");
     }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperInterfaceTestTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperInterfaceTestTest.java
index c853a99..e5ffadc 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperInterfaceTestTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperInterfaceTestTest.java
@@ -4,12 +4,13 @@
 
 package com.android.tools.r8.repackage;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -26,16 +27,16 @@
 
   @Test
   public void testR8WithoutRepackaging() throws Exception {
-    runTest(false).assertSuccessWithOutputLines(EXPECTED);
+    runTest(false);
   }
 
   @Test
   public void testR8() throws Exception {
-    runTest(true).assertSuccessWithOutputLines(EXPECTED);
+    runTest(true);
   }
 
-  private R8TestRunResult runTest(boolean repackage) throws Exception {
-    return testForR8(parameters.getBackend())
+  private void runTest(boolean repackage) throws Exception {
+    testForR8(parameters.getBackend())
         .addProgramClasses(ClassImplementingMissingInterface.class, Main.class)
         .addKeepMainRule(Main.class)
         .applyIf(repackage, this::configureRepackaging)
@@ -46,13 +47,20 @@
         .compile()
         .inspect(
             inspector -> {
-              assertThat(ClassImplementingMissingInterface.class, isNotRepackaged(inspector));
+              if (repackage) {
+                assertThat(ClassImplementingMissingInterface.class, isRepackaged(inspector));
+              } else {
+                // The class is minified.
+                ClassSubject clazz = inspector.clazz(ClassImplementingMissingInterface.class);
+                assertThat(clazz, isPresentAndRenamed());
+              }
             })
         .addRunClasspathClasses(MissingInterface.class)
-        .run(parameters.getRuntime(), Main.class);
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
   }
 
-  private interface MissingInterface {
+  public interface MissingInterface {
 
     void bar();
   }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
index 5880f86..b903e37 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
@@ -8,7 +8,6 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,16 +31,16 @@
 
   @Test
   public void testR8WithoutRepackaging() throws Exception {
-    runTest(false).assertSuccessWithOutputLines(EXPECTED);
+    runTest(false);
   }
 
   @Test
   public void testR8() throws Exception {
-    runTest(true).assertSuccessWithOutputLines(EXPECTED);
+    runTest(true);
   }
 
-  private R8TestRunResult runTest(boolean repackage) throws Exception {
-    return testForR8(parameters.getBackend())
+  private void runTest(boolean repackage) throws Exception {
+    testForR8(parameters.getBackend())
         .addProgramClasses(
             ClassWithSuperCall.class,
             ClassWithoutSuperCall.class,
@@ -56,14 +55,15 @@
         .compile()
         .inspect(
             inspector -> {
-              assertThat(ClassWithSuperCall.class, isNotRepackaged(inspector));
-              assertThat(ClassWithoutSuperCall.class, isNotRepackaged(inspector));
+              assertThat(ClassWithSuperCall.class, isRepackagedIf(inspector, repackage));
+              assertThat(ClassWithoutSuperCall.class, isRepackagedIf(inspector, repackage));
             })
         .addRunClasspathClasses(MissingSuperType.class)
-        .run(parameters.getRuntime(), Main.class);
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
   }
 
-  static class MissingSuperType {
+  public static class MissingSuperType {
 
     public void foo() {
       System.out.println("MissingSuperType::foo");