Reland "Implicitly add necessary rules for Java reflections in any R8 mode."

PS1: same as original: https://r8-review.googlesource.com/c/r8/+/24260
PS4: rebase to a fix: https://r8-review.googlesource.com/c/r8/+/24424
PS6: fix remaining tests
PS7: not store -identifiernamestring rules for compat Proguard config

Bug: 76181966, 111619623
Change-Id: I3b5c6e99ff7626a95b655252cf2c98fd418e0e27
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index d92de6b..8714b28 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -26,15 +26,15 @@
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.ProguardClassFilter;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 import java.util.Map;
-import java.util.Set;
 
 class IdentifierMinifier {
 
   private final AppInfoWithLiveness appInfo;
   private final ProguardClassFilter adaptClassStrings;
   private final NamingLens lens;
-  private final Set<DexItem> identifierNameStrings;
+  private final Object2BooleanMap<DexItem> identifierNameStrings;
 
   IdentifierMinifier(
       AppInfoWithLiveness appInfo, ProguardClassFilter adaptClassStrings, NamingLens lens) {
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index b0f4dc0..830391a 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -35,17 +35,17 @@
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Streams;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 import java.util.Arrays;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Objects;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 public class IdentifierNameStringMarker {
   private final AppInfo appInfo;
   private final DexItemFactory dexItemFactory;
-  private final Set<DexItem> identifierNameStrings;
+  private final Object2BooleanMap<DexItem> identifierNameStrings;
   private final InternalOptions options;
 
   public IdentifierNameStringMarker(AppInfoWithLiveness appInfo, InternalOptions options) {
@@ -63,7 +63,7 @@
   }
 
   private void decoupleIdentifierNameStringInField(DexEncodedField encodedField) {
-    if (!identifierNameStrings.contains(encodedField.field)) {
+    if (!identifierNameStrings.containsKey(encodedField.field)) {
       return;
     }
     if (!encodedField.accessFlags.isStatic()) {
@@ -102,22 +102,27 @@
         if (instruction.isStaticPut() || instruction.isInstancePut()) {
           FieldInstruction fieldPut = instruction.asFieldInstruction();
           DexField field = fieldPut.getField();
-          if (!identifierNameStrings.contains(field)) {
+          if (!identifierNameStrings.containsKey(field)) {
             continue;
           }
+          boolean isExplicitRule = identifierNameStrings.getBoolean(field);
           Value in = instruction.isStaticPut()
               ? instruction.asStaticPut().inValue()
               : instruction.asInstancePut().value();
           if (!in.isConstString()) {
-            warnUndeterminedIdentifierIfNecessary(
-                appInfo, options, field, originHolder, instruction, null);
+            if (isExplicitRule) {
+              warnUndeterminedIdentifierIfNecessary(
+                  appInfo, options, field, originHolder, instruction, null);
+            }
             continue;
           }
           DexString original = in.getConstInstruction().asConstString().getValue();
           DexItemBasedString itemBasedString = inferMemberOrTypeFromNameString(appInfo, original);
           if (itemBasedString == null) {
-            warnUndeterminedIdentifierIfNecessary(
-                appInfo, options, field, originHolder, instruction, original);
+            if (isExplicitRule) {
+              warnUndeterminedIdentifierIfNecessary(
+                  appInfo, options, field, originHolder, instruction, original);
+            }
             continue;
           }
           // Move the cursor back to $fieldPut
@@ -163,16 +168,19 @@
         } else if (instruction.isInvokeMethod()) {
           InvokeMethod invoke = instruction.asInvokeMethod();
           DexMethod invokedMethod = invoke.getInvokedMethod();
-          if (!identifierNameStrings.contains(invokedMethod)) {
+          if (!identifierNameStrings.containsKey(invokedMethod)) {
             continue;
           }
+          boolean isExplicitRule = identifierNameStrings.getBoolean(invokedMethod);
           List<Value> ins = invoke.arguments();
           Value[] changes = new Value [ins.size()];
           if (isReflectionMethod(dexItemFactory, invokedMethod)) {
             DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke);
             if (itemBasedString == null) {
-              warnUndeterminedIdentifierIfNecessary(
-                  appInfo, options, invokedMethod, originHolder, instruction, null);
+              if (isExplicitRule) {
+                warnUndeterminedIdentifierIfNecessary(
+                    appInfo, options, invokedMethod, originHolder, instruction, null);
+              }
               continue;
             }
             DexType returnType = invoke.getReturnType();
@@ -217,16 +225,20 @@
             for (int i = 0; i < ins.size(); i++) {
               Value in = ins.get(i);
               if (!in.isConstString()) {
-                warnUndeterminedIdentifierIfNecessary(
-                    appInfo, options, invokedMethod, originHolder, instruction, null);
+                if (isExplicitRule) {
+                  warnUndeterminedIdentifierIfNecessary(
+                      appInfo, options, invokedMethod, originHolder, instruction, null);
+                }
                 continue;
               }
               DexString original = in.getConstInstruction().asConstString().getValue();
               DexItemBasedString itemBasedString =
                   inferMemberOrTypeFromNameString(appInfo, original);
               if (itemBasedString == null) {
-                warnUndeterminedIdentifierIfNecessary(
-                    appInfo, options, invokedMethod, originHolder, instruction, original);
+                if (isExplicitRule) {
+                  warnUndeterminedIdentifierIfNecessary(
+                      appInfo, options, invokedMethod, originHolder, instruction, original);
+                }
                 continue;
               }
               // Move the cursor back to $invoke
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 bcd489e..420af47 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5,8 +5,6 @@
 
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
-import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifierIfNecessary;
-import static com.android.tools.r8.shaking.ProguardConfigurationUtils.buildIdentifierNameStringRule;
 
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.dex.IndexedItemCollection;
@@ -58,6 +56,8 @@
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets.SetView;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.util.ArrayDeque;
 import java.util.Collection;
@@ -187,10 +187,9 @@
   private final Queue<Action> proguardCompatibilityWorkList = Queues.newArrayDeque();
 
   /**
-   * A set of methods that need code inspection for Proguard compatibility rules.
+   * A set of methods that need code inspection for Java reflection in use.
    */
-  private final Set<DexEncodedMethod> pendingProguardReflectiveCompatibility =
-      Sets.newLinkedHashSet();
+  private final Set<DexEncodedMethod> pendingReflectiveUses = Sets.newLinkedHashSet();
 
   /**
    * A cache for DexMethod that have been marked reachable.
@@ -314,13 +313,10 @@
 
     boolean registerInvokeVirtual(DexMethod method, KeepReason keepReason) {
       if (appInfo.dexItemFactory.classMethods.isReflectiveMemberLookup(method)) {
-        if (forceProguardCompatibility) {
-          // TODO(b/76181966): whether or not add this rule in normal mode.
-          if (identifierNameStrings.add(method) && compatibility != null) {
-            compatibility.addRule(buildIdentifierNameStringRule(method));
-          }
-          pendingProguardReflectiveCompatibility.add(currentMethod);
-        }
+        // Implicitly add -identifiernamestring rule for the Java reflection in use.
+        identifierNameStrings.add(method);
+        // Revisit the current method to implicitly add -keep rule for items with reflective access.
+        pendingReflectiveUses.add(currentMethod);
       }
       if (!registerItemWithTarget(virtualInvokes, method)) {
         return false;
@@ -356,13 +352,10 @@
     boolean registerInvokeStatic(DexMethod method, KeepReason keepReason) {
       if (method == appInfo.dexItemFactory.classMethods.forName
           || appInfo.dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(method)) {
-        if (forceProguardCompatibility) {
-          // TODO(b/76181966): whether or not add this rule in normal mode.
-          if (identifierNameStrings.add(method) && compatibility != null) {
-            compatibility.addRule(buildIdentifierNameStringRule(method));
-          }
-          pendingProguardReflectiveCompatibility.add(currentMethod);
-        }
+        // Implicitly add -identifiernamestring rule for the Java reflection in use.
+        identifierNameStrings.add(method);
+        // Revisit the current method to implicitly add -keep rule for items with reflective access.
+        pendingReflectiveUses.add(currentMethod);
       }
       if (!registerItemWithTarget(staticInvokes, method)) {
         return false;
@@ -1279,15 +1272,15 @@
         }
 
         // Continue fix-point processing while there are additional work items to ensure
-        // Proguard compatibility.
+        // items that are passed to Java reflections are traced.
         if (proguardCompatibilityWorkList.isEmpty()
-            && pendingProguardReflectiveCompatibility.isEmpty()) {
+            && pendingReflectiveUses.isEmpty()) {
           break;
         }
-        pendingProguardReflectiveCompatibility.forEach(this::handleProguardReflectiveBehavior);
+        pendingReflectiveUses.forEach(this::handleReflectiveBehavior);
         workList.addAll(proguardCompatibilityWorkList);
         proguardCompatibilityWorkList.clear();
-        pendingProguardReflectiveCompatibility.clear();
+        pendingReflectiveUses.clear();
       }
       if (Log.ENABLED) {
         Set<DexEncodedMethod> allLive = Sets.newIdentityHashSet();
@@ -1484,15 +1477,15 @@
         Action.markMethodLive(method, KeepReason.dueToProguardCompatibilityKeepRule(rule)));
   }
 
-  private void handleProguardReflectiveBehavior(DexEncodedMethod method) {
+  private void handleReflectiveBehavior(DexEncodedMethod method) {
     DexType originHolder = method.method.holder;
     Origin origin = appInfo.originFor(originHolder);
     IRCode code = method.buildIR(appInfo, graphLense, options, origin);
     code.instructionIterator().forEachRemaining(instr ->
-        handleProguardReflectiveBehavior(instr, originHolder));
+        handleReflectiveBehavior(instr, originHolder));
   }
 
-  private void handleProguardReflectiveBehavior(Instruction instruction, DexType originHolder) {
+  private void handleReflectiveBehavior(Instruction instruction, DexType originHolder) {
     if (!instruction.isInvokeMethod()) {
       return;
     }
@@ -1503,8 +1496,6 @@
     }
     DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke);
     if (itemBasedString == null) {
-      warnUndeterminedIdentifierIfNecessary(
-          appInfo, options, invokedMethod, originHolder, instruction, null);
       return;
     }
     if (itemBasedString.basedOn instanceof DexType) {
@@ -1692,8 +1683,10 @@
     public final Set<DexItem> neverInline;
     /**
      * All items with -identifiernamestring rule.
+     * Bound boolean value indicates the rule is explicitly specified by users (<code>true</code>)
+     * or not, i.e., implicitly added by R8 (<code>false</code>).
      */
-    public final Set<DexItem> identifierNameStrings;
+    public final Object2BooleanMap<DexItem> identifierNameStrings;
     /**
      * Set of fields that have been identified as proto-lite fields by the
      * {@link ProtoLiteExtension}.
@@ -1750,8 +1743,8 @@
       this.alwaysInline = enqueuer.rootSet.alwaysInline;
       this.forceInline = enqueuer.rootSet.forceInline;
       this.neverInline = enqueuer.rootSet.neverInline;
-      this.identifierNameStrings =
-          Sets.union(enqueuer.rootSet.identifierNameStrings, enqueuer.identifierNameStrings);
+      this.identifierNameStrings = joinIdentifierNameStrings(
+          enqueuer.rootSet.identifierNameStrings, enqueuer.identifierNameStrings);
       this.protoLiteFields = enqueuer.protoLiteFields;
       this.prunedTypes = Collections.emptySet();
       this.switchMaps = Collections.emptyMap();
@@ -1922,6 +1915,18 @@
           .collect(ImmutableSortedSet.toImmutableSortedSet(PresortedComparable::slowCompare));
     }
 
+    private Object2BooleanMap<DexItem> joinIdentifierNameStrings(
+        Set<DexItem> explicit, Set<DexItem> implicit) {
+      Object2BooleanMap<DexItem> result = new Object2BooleanArrayMap<>();
+      for (DexItem e : explicit) {
+        result.putIfAbsent(e, true);
+      }
+      for (DexItem i : implicit) {
+        result.putIfAbsent(i, false);
+      }
+      return result;
+    }
+
     private <T extends PresortedComparable<T>> SortedSet<T> toSortedDescriptorSet(
         Set<? extends KeyedDexItem<T>> set) {
       ImmutableSortedSet.Builder<T> builder =
@@ -2031,6 +2036,32 @@
       return builder.build();
     }
 
+    private static Object2BooleanMap<DexItem> rewriteMixedItemsConservatively(
+        Object2BooleanMap<DexItem> original, GraphLense lense) {
+      Object2BooleanMap<DexItem> result = new Object2BooleanArrayMap<>();
+      for (Object2BooleanMap.Entry<DexItem> entry : original.object2BooleanEntrySet()) {
+        DexItem item = entry.getKey();
+        // TODO(b/67934123) There should be a common interface to perform the dispatch.
+        if (item instanceof DexType) {
+          result.put(lense.lookupType((DexType) item), entry.getBooleanValue());
+        } else if (item instanceof DexMethod) {
+          DexMethod method = (DexMethod) item;
+          if (lense.isContextFreeForMethod(method)) {
+            result.put(lense.lookupMethod(method), entry.getBooleanValue());
+          } else {
+            for (DexMethod candidate: lense.lookupMethodInAllContexts(method)) {
+              result.put(candidate, entry.getBooleanValue());
+            }
+          }
+        } else if (item instanceof DexField) {
+          result.put(lense.lookupField((DexField) item), entry.getBooleanValue());
+        } else {
+          throw new Unreachable();
+        }
+      }
+      return result;
+    }
+
     private static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) {
       ImmutableSet.Builder<T> builder = ImmutableSet.builder();
       builder.addAll(first);
diff --git a/src/test/examples/identifiernamestring/keep-rules-3.txt b/src/test/examples/identifiernamestring/keep-rules-3.txt
index aa23772..1c69314 100644
--- a/src/test/examples/identifiernamestring/keep-rules-3.txt
+++ b/src/test/examples/identifiernamestring/keep-rules-3.txt
@@ -11,7 +11,7 @@
 
 -dontshrink
 
--identifiernamestring class * {
+-identifiernamestring class **.R {
   static java.lang.reflect.Field *(java.lang.Class, java.lang.String);
   static java.lang.reflect.Method *(java.lang.Class, java.lang.String, java.lang.Class[]);
 }
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index a9ee851..73cdaaf 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -142,7 +142,7 @@
     FoundMethodSubject foundMain = (FoundMethodSubject) main;
     verifyPresenceOfConstString(foundMain);
     int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain);
-    assertEquals(0, renamedYetFoundIdentifierCount);
+    assertEquals(4, renamedYetFoundIdentifierCount);
 
     ClassSubject aClass = inspector.clazz("adaptclassstrings.A");
     MethodSubject bar = aClass.method("void", "bar", ImmutableList.of());
@@ -165,7 +165,7 @@
     FoundMethodSubject foundMain = (FoundMethodSubject) main;
     verifyPresenceOfConstString(foundMain);
     int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain);
-    assertEquals(0, renamedYetFoundIdentifierCount);
+    assertEquals(4, renamedYetFoundIdentifierCount);
 
     ClassSubject aClass = inspector.clazz("adaptclassstrings.A");
     MethodSubject bar = aClass.method("void", "bar", ImmutableList.of());
@@ -231,7 +231,7 @@
     FoundMethodSubject foundMain = (FoundMethodSubject) main;
     verifyPresenceOfConstString(foundMain);
     int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain);
-    assertEquals(0, renamedYetFoundIdentifierCount);
+    assertEquals(1, renamedYetFoundIdentifierCount);
 
     ClassSubject aClass = inspector.clazz("identifiernamestring.A");
     MethodSubject aInit =
@@ -255,7 +255,7 @@
     FoundMethodSubject foundMain = (FoundMethodSubject) main;
     verifyPresenceOfConstString(foundMain);
     int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain);
-    assertEquals(1, renamedYetFoundIdentifierCount);
+    assertEquals(2, renamedYetFoundIdentifierCount);
 
     ClassSubject aClass = inspector.clazz("identifiernamestring.A");
     MethodSubject aInit =
@@ -271,7 +271,7 @@
     assertEquals(2, renamedYetFoundIdentifierCount);
   }
 
-  // With -identifiernamestring for reflective methods
+  // With -identifiernamestring for reflective methods in testing class R.
   private static void test2_rule3(CodeInspector inspector) {
     ClassSubject mainClass = inspector.clazz("identifiernamestring.Main");
     MethodSubject main = mainClass.method(CodeInspector.MAIN);
diff --git a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
index 829cdd0..49ba186 100644
--- a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
+++ b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
@@ -9,6 +9,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.DiagnosticsChecker;
 import com.android.tools.r8.OutputMode;
@@ -72,15 +73,17 @@
       byte[][] classes,
       Class main,
       Path out,
+      boolean explicitRule,
       boolean enableMinification,
       boolean forceProguardCompatibility)
       throws Exception {
     String minificationModifier = enableMinification ? ",allowobfuscation " : " ";
-    String reflectionRules = forceProguardCompatibility ? ""
-        : "-identifiernamestring class java.lang.Class {\n"
+    String reflectionRules = explicitRule
+        ? "-identifiernamestring class java.lang.Class {\n"
             + "static java.lang.Class forName(java.lang.String);\n"
             + "java.lang.reflect.Method getDeclaredMethod(java.lang.String, java.lang.Class[]);\n"
-            + "}\n";
+            + "}\n"
+        : "";
     R8Command.Builder commandBuilder =
         R8Command.builder(reporter)
             .addProguardConfiguration(
@@ -102,7 +105,8 @@
     });
   }
 
-  private void reflectionWithBuildter(
+  private void reflectionWithBuilder(
+      boolean explicitRule,
       boolean enableMinification,
       boolean forceProguardCompatibility) throws Exception {
     byte[][] classes = {
@@ -110,7 +114,7 @@
     };
     Path out = temp.getRoot().toPath();
     AndroidApp processedApp = runR8(classes, WarnReflectiveAccessTestMain.class, out,
-        enableMinification, forceProguardCompatibility);
+        explicitRule, enableMinification, forceProguardCompatibility);
 
     String main = WarnReflectiveAccessTestMain.class.getCanonicalName();
     CodeInspector codeInspector = new CodeInspector(processedApp);
@@ -133,35 +137,59 @@
   }
 
   @Test
-  public void test_minification_forceProguardCompatibility() throws Exception {
-    reflectionWithBuildter(true, true);
+  public void test_explicit_minification_forceProguardCompatibility() throws Exception {
+    reflectionWithBuilder(true, true, true);
     assertFalse(handler.warnings.isEmpty());
-    DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 54, 1,
-        "Cannot determine", "getDeclaredMethod", "resolution failure");
-  }
-
-  @Test
-  public void test_noMinification_forceProguardCompatibility() throws Exception {
-    reflectionWithBuildter(false, true);
-    assertFalse(handler.warnings.isEmpty());
-    DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 54, 1,
-        "Cannot determine", "getDeclaredMethod", "resolution failure");
-  }
-
-  @Test
-  public void test_minification_R8() throws Exception {
-    reflectionWithBuildter(true, false);
-    assertFalse(handler.warnings.isEmpty());
-    DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 54, 1,
+    DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 55, 1,
         "Cannot determine", "getDeclaredMethod", "-identifiernamestring", "resolution failure");
   }
 
   @Test
-  public void test_noMinification_R8() throws Exception {
-    reflectionWithBuildter(false, false);
+  public void test_explicit_noMinification_forceProguardCompatibility() throws Exception {
+    reflectionWithBuilder(true, false, true);
     assertFalse(handler.warnings.isEmpty());
-    DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 54, 1,
+    DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 55, 1,
         "Cannot determine", "getDeclaredMethod", "-identifiernamestring", "resolution failure");
   }
 
+  @Test
+  public void test_explicit_minification_R8() throws Exception {
+    reflectionWithBuilder(true, true, false);
+    assertFalse(handler.warnings.isEmpty());
+    DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 55, 1,
+        "Cannot determine", "getDeclaredMethod", "-identifiernamestring", "resolution failure");
+  }
+
+  @Test
+  public void test_explicit_noMinification_R8() throws Exception {
+    reflectionWithBuilder(true, false, false);
+    assertFalse(handler.warnings.isEmpty());
+    DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 55, 1,
+        "Cannot determine", "getDeclaredMethod", "-identifiernamestring", "resolution failure");
+  }
+
+  @Test
+  public void test_implicit_minification_forceProguardCompatibility() throws Exception {
+    reflectionWithBuilder(false, true, true);
+    assertTrue(handler.warnings.isEmpty());
+  }
+
+  @Test
+  public void test_implicit_noMinification_forceProguardCompatibility() throws Exception {
+    reflectionWithBuilder(false, false, true);
+    assertTrue(handler.warnings.isEmpty());
+  }
+
+  @Test
+  public void test_implicit_minification_R8() throws Exception {
+    reflectionWithBuilder(false, true, false);
+    assertTrue(handler.warnings.isEmpty());
+  }
+
+  @Test
+  public void test_implicit_noMinification_R8() throws Exception {
+    reflectionWithBuilder(false, false, false);
+    assertTrue(handler.warnings.isEmpty());
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 51baed2..9cf0a85 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
-import com.android.tools.r8.shaking.ProguardIdentifierNameStringRule;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.shaking.ProguardKeepRule;
 import com.android.tools.r8.shaking.ProguardKeepRuleType;
@@ -261,7 +260,7 @@
     assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
     forNameClasses.forEach(clazz -> {
       ClassSubject subject = inspector.clazz(getJavacGeneratedClassName(clazz));
-      assertEquals(forceProguardCompatibility, subject.isPresent());
+      assertTrue(subject.isPresent());
       assertEquals(subject.isPresent() && allowObfuscation, subject.isRenamed());
     });
 
@@ -273,8 +272,10 @@
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
     if (forceProguardCompatibility) {
       List<ProguardConfigurationRule> rules = configuration.getRules();
-      assertEquals(3, rules.size());
-      Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> {
+      assertEquals(2, rules.size());
+      for (ProguardConfigurationRule r : rules) {
+        assertTrue(r instanceof ProguardKeepRule);
+        ProguardKeepRule rule = (ProguardKeepRule) r;
         assertEquals(ProguardKeepRuleType.KEEP, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
@@ -292,18 +293,7 @@
           assertEquals(1, memberRules.size());
           assertEquals(ProguardMemberType.INIT, memberRules.iterator().next().getRuleType());
         }
-      });
-      Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        List<ProguardMemberRule> memberRules = rule.getMemberRules();
-        ProguardClassNameList classNames = rule.getClassNames();
-        assertEquals(1, classNames.size());
-        DexType type = classNames.asSpecificDexTypes().get(0);
-        assertEquals("java.lang.Class", type.toSourceString());
-        assertEquals(1, memberRules.size());
-        ProguardMemberRule memberRule = memberRules.iterator().next();
-        assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
-        assertEquals("forName", memberRule.getName().toString());
-      });
+      }
     } else {
       assertEquals(0, configuration.getRules().size());
     }
@@ -367,11 +357,11 @@
     assertTrue(classSubject.isPresent());
     assertEquals(allowObfuscation, classSubject.isRenamed());
     FieldSubject foo = classSubject.field("java.lang.String", "foo");
-    assertEquals(forceProguardCompatibility, foo.isPresent());
+    assertTrue(foo.isPresent());
     assertEquals(foo.isPresent() && allowObfuscation, foo.isRenamed());
     MethodSubject bar =
         classSubject.method("java.lang.String", "bar", ImmutableList.of("java.lang.String"));
-    assertEquals(forceProguardCompatibility, bar.isPresent());
+    assertTrue(bar.isPresent());
     assertEquals(bar.isPresent() && allowObfuscation, bar.isRenamed());
 
     // Check the Proguard compatibility rules generated.
@@ -382,8 +372,10 @@
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
     if (forceProguardCompatibility) {
       List<ProguardConfigurationRule> rules = configuration.getRules();
-      assertEquals(4, rules.size());
-      Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> {
+      assertEquals(2, rules.size());
+      for (ProguardConfigurationRule r : rules) {
+        assertTrue(r instanceof ProguardKeepRule);
+        ProguardKeepRule rule = (ProguardKeepRule) r;
         assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
@@ -400,18 +392,7 @@
           assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
           assertTrue(memberRule.getName().matches("bar"));
         }
-      });
-      Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        List<ProguardMemberRule> memberRules = rule.getMemberRules();
-        ProguardClassNameList classNames = rule.getClassNames();
-        assertEquals(1, classNames.size());
-        DexType type = classNames.asSpecificDexTypes().get(0);
-        assertEquals("java.lang.Class", type.toSourceString());
-        assertEquals(1, memberRules.size());
-        ProguardMemberRule memberRule = memberRules.iterator().next();
-        assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
-        assertTrue(memberRule.getName().toString().startsWith("getDeclared"));
-      });
+      }
     } else {
       assertEquals(0, configuration.getRules().size());
     }
@@ -480,13 +461,13 @@
     assertTrue(classSubject.isPresent());
     assertEquals(allowObfuscation, classSubject.isRenamed());
     FieldSubject f = classSubject.field("int", "intField");
-    assertEquals(forceProguardCompatibility, f.isPresent());
+    assertTrue(f.isPresent());
     assertEquals(f.isPresent() && allowObfuscation, f.isRenamed());
     f = classSubject.field("long", "longField");
-    assertEquals(forceProguardCompatibility, f.isPresent());
+    assertTrue(f.isPresent());
     assertEquals(f.isPresent() && allowObfuscation, f.isRenamed());
     f = classSubject.field("java.lang.Object", "objField");
-    assertEquals(forceProguardCompatibility, f.isPresent());
+    assertTrue(f.isPresent());
     assertEquals(f.isPresent() && allowObfuscation, f.isRenamed());
 
     // Check the Proguard compatibility rules generated.
@@ -497,9 +478,11 @@
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
     if (forceProguardCompatibility) {
       List<ProguardConfigurationRule> rules = configuration.getRules();
-      assertEquals(6, rules.size());
+      assertEquals(3, rules.size());
       Object2BooleanMap<String> keptFields = new Object2BooleanArrayMap<>();
-      Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> {
+      for (ProguardConfigurationRule r : rules) {
+        assertTrue(r instanceof ProguardKeepRule);
+        ProguardKeepRule rule = (ProguardKeepRule) r;
         assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
@@ -512,23 +495,11 @@
         ProguardMemberRule memberRule = memberRules.iterator().next();
         assertEquals(ProguardMemberType.FIELD, memberRule.getRuleType());
         keptFields.put(memberRule.getName().toString(), true);
-      });
+      }
       assertEquals(3, keptFields.size());
       assertTrue(keptFields.containsKey("intField"));
       assertTrue(keptFields.containsKey("longField"));
       assertTrue(keptFields.containsKey("objField"));
-      Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        List<ProguardMemberRule> memberRules = rule.getMemberRules();
-        ProguardClassNameList classNames = rule.getClassNames();
-        assertEquals(1, classNames.size());
-        DexType type = classNames.asSpecificDexTypes().get(0);
-        assertTrue(type.toSourceString().startsWith("java.util.concurrent.atomic.Atomic"));
-        assertTrue(type.toSourceString().endsWith("FieldUpdater"));
-        assertEquals(1, memberRules.size());
-        ProguardMemberRule memberRule = memberRules.iterator().next();
-        assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
-        assertEquals("newUpdater", memberRule.getName().toString());
-      });
     } else {
       assertEquals(0, configuration.getRules().size());
     }