Implement support for allowshrinking as soft pinning.

Bug: 171289133
Change-Id: I399e71ee750d73ffb499ee5be5ce993bd1a388c9
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 6a8520e..5cf69c1 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -55,6 +55,7 @@
 import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
 import com.android.tools.r8.graph.PresortedComparable;
 import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.graph.ResolutionResult.FailedResolutionResult;
@@ -89,6 +90,7 @@
 import com.android.tools.r8.shaking.KeepInfoCollection.MutableKeepInfoCollection;
 import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
 import com.android.tools.r8.shaking.RootSetBuilder.ItemsWithRules;
+import com.android.tools.r8.shaking.RootSetBuilder.MutableItemsWithRules;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
 import com.android.tools.r8.utils.Action;
@@ -2336,6 +2338,8 @@
     // Add all dependent members to the workqueue.
     enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
 
+    checkMemberForSoftPinning(field);
+
     // Notify analyses.
     analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
   }
@@ -2352,6 +2356,8 @@
     // Add all dependent members to the workqueue.
     enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
 
+    checkMemberForSoftPinning(field);
+
     // Notify analyses.
     analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
   }
@@ -3337,12 +3343,26 @@
             enqueueRootItems(dependentItems);
           }
         });
+    consequentRootSet.dependentSoftPinned.forEach(
+        (reference, dependentItems) -> {
+          if (isLiveProgramReference(reference)) {
+            dependentItems.forEachReference(
+                item -> {
+                  if (isLiveProgramReference(item)) {
+                    keepInfo.joinInfo(item, appView, Joiner::pin);
+                  }
+                });
+          }
+        });
+
     // TODO(b/132600955): This modifies the root set. Should the consequent be persistent?
     rootSet.addConsequentRootSet(consequentRootSet, addNoShrinking);
     if (mode.isInitialTreeShaking()) {
       for (DexReference reference : consequentRootSet.noObfuscation) {
         keepInfo.evaluateRule(reference, appView, Joiner::disallowMinification);
       }
+      consequentRootSet.softPinned.forEachReference(
+          reference -> keepInfo.evaluateRule(reference, appView, Joiner::pin));
     }
     enqueueRootItems(consequentRootSet.noShrinking);
     // Check for compatibility rules indicating that the holder must be implicitly kept.
@@ -3356,6 +3376,18 @@
     }
   }
 
+  private boolean isLiveProgramReference(DexReference reference) {
+    if (reference.isDexType()) {
+      DexProgramClass clazz =
+          DexProgramClass.asProgramClassOrNull(definitionFor(reference.asDexType()));
+      return clazz != null && isTypeLive(clazz);
+    }
+    DexMember<?, ?> member = reference.asDexMember();
+    DexProgramClass holder = DexProgramClass.asProgramClassOrNull(definitionFor(member.holder));
+    ProgramMember<?, ?> programMember = member.lookupOnProgramClass(holder);
+    return programMember != null && isMemberLive(programMember.getDefinition());
+  }
+
   private ConsequentRootSet computeDelayedInterfaceMethodSyntheticBridges() {
     RootSetBuilder builder = new RootSetBuilder(appView, subtypingInfo);
     for (DelayedRootSetActionItem delayedRootSetActionItem : rootSet.delayedRootSetActionItems) {
@@ -3556,10 +3588,28 @@
     // Add all dependent members to the workqueue.
     enqueueRootItems(rootSet.getDependentItems(definition));
 
+    checkMemberForSoftPinning(method);
+
     // Notify analyses.
     analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method));
   }
 
+  private void checkMemberForSoftPinning(ProgramMember<?, ?> member) {
+    DexMember<?, ?> reference = member.getDefinition().toReference();
+    Set<ProguardKeepRuleBase> softPinRules = rootSet.softPinned.getRulesForReference(reference);
+    if (softPinRules != null) {
+      assert softPinRules.stream().noneMatch(r -> r.getModifiers().allowsOptimization);
+      keepInfo.joinInfo(reference, appInfo, Joiner::pin);
+    }
+    // Identify dependent soft pinning.
+    MutableItemsWithRules items = rootSet.dependentSoftPinned.get(member.getHolderType());
+    if (items != null && items.containsReference(reference)) {
+      assert items.getRulesForReference(reference).stream()
+          .noneMatch(r -> r.getModifiers().allowsOptimization);
+      keepInfo.joinInfo(reference, appInfo, Joiner::pin);
+    }
+  }
+
   private void markReferencedTypesAsLive(ProgramMethod method) {
     markTypeAsLive(
         method.getHolderType(), clazz -> graphReporter.reportClassReferencedFrom(clazz, method));
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index f8b5987..1f588d8 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -83,6 +83,7 @@
   private final DirectMappedDexApplication application;
   private final Iterable<? extends ProguardConfigurationRule> rules;
   private final MutableItemsWithRules noShrinking = new MutableItemsWithRules();
+  private final MutableItemsWithRules softPinned = new MutableItemsWithRules();
   private final Set<DexReference> noObfuscation = Sets.newIdentityHashSet();
   private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
   private final LinkedHashMap<DexReference, DexReference> checkDiscarded = new LinkedHashMap<>();
@@ -103,6 +104,8 @@
   private final Set<DexReference> neverPropagateValue = Sets.newIdentityHashSet();
   private final Map<DexReference, MutableItemsWithRules> dependentNoShrinking =
       new IdentityHashMap<>();
+  private final Map<DexReference, MutableItemsWithRules> dependentSoftPinned =
+      new IdentityHashMap<>();
   private final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule =
       new IdentityHashMap<>();
   private final Map<DexReference, ProguardMemberRule> mayHaveSideEffects = new IdentityHashMap<>();
@@ -334,6 +337,7 @@
     assert appView.options().isMinificationEnabled() || noObfuscation.isEmpty();
     return new RootSet(
         noShrinking,
+        softPinned,
         noObfuscation,
         ImmutableList.copyOf(reasonAsked.values()),
         ImmutableList.copyOf(checkDiscarded.values()),
@@ -356,6 +360,7 @@
         noSideEffects,
         assumedValues,
         dependentNoShrinking,
+        dependentSoftPinned,
         dependentKeepClassCompatRule,
         identifierNameStrings,
         ifRules,
@@ -424,8 +429,10 @@
         neverInline,
         neverClassInline,
         noShrinking,
+        softPinned,
         noObfuscation,
         dependentNoShrinking,
+        dependentSoftPinned,
         dependentKeepClassCompatRule,
         Lists.newArrayList(delayedRootSetActionItems));
   }
@@ -1042,6 +1049,7 @@
     dependentNoShrinking
         .computeIfAbsent(item.toReference(), x -> new MutableItemsWithRules())
         .addClassWithRule(type, context);
+    // TODO(b/171289133): Test and implement allowshrinking and use of -includedescriptorclasses
     // Unconditionally add to no-obfuscation, as that is only checked for surviving items.
     if (appView.options().isMinificationEnabled()) {
       noObfuscation.add(type);
@@ -1136,6 +1144,14 @@
           noShrinking.addReferenceWithRule(item.toReference(), keepRule);
         }
         context.markAsUsed();
+      } else if (!modifiers.allowsOptimization) {
+        if (precondition != null) {
+          dependentSoftPinned
+              .computeIfAbsent(precondition.toReference(), x -> new MutableItemsWithRules())
+              .addReferenceWithRule(item.toReference(), keepRule);
+        } else {
+          softPinned.addReferenceWithRule(item.toReference(), keepRule);
+        }
       }
       if (!modifiers.allowsOptimization) {
         // The -dontoptimize flag has only effect through the keep all rule, but we still
@@ -1301,8 +1317,10 @@
     final Set<DexMethod> neverInline;
     final Set<DexType> neverClassInline;
     final MutableItemsWithRules noShrinking;
+    final MutableItemsWithRules softPinned;
     final Set<DexReference> noObfuscation;
     final Map<DexReference, MutableItemsWithRules> dependentNoShrinking;
+    final Map<DexReference, MutableItemsWithRules> dependentSoftPinned;
     final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
     final List<DelayedRootSetActionItem> delayedRootSetActionItems;
 
@@ -1310,15 +1328,19 @@
         Set<DexMethod> neverInline,
         Set<DexType> neverClassInline,
         MutableItemsWithRules noShrinking,
+        MutableItemsWithRules softPinned,
         Set<DexReference> noObfuscation,
         Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+        Map<DexReference, MutableItemsWithRules> dependentSoftPinned,
         Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
         List<DelayedRootSetActionItem> delayedRootSetActionItems) {
       this.neverInline = neverInline;
       this.neverClassInline = neverClassInline;
       this.noShrinking = noShrinking;
+      this.softPinned = softPinned;
       this.noObfuscation = noObfuscation;
       this.dependentNoShrinking = dependentNoShrinking;
+      this.dependentSoftPinned = dependentSoftPinned;
       this.dependentKeepClassCompatRule = dependentKeepClassCompatRule;
       this.delayedRootSetActionItems = delayedRootSetActionItems;
     }
@@ -1445,19 +1467,20 @@
       return reference.apply(this::containsClass, this::containsField, this::containsMethod);
     }
 
-    public abstract void forEachClass(Consumer<DexType> consumer);
+    public abstract void forEachClass(Consumer<? super DexType> consumer);
 
-    public abstract void forEachClass(BiConsumer<DexType, Set<ProguardKeepRuleBase>> consumer);
+    public abstract void forEachClass(
+        BiConsumer<? super DexType, Set<ProguardKeepRuleBase>> consumer);
 
     public abstract void forEachField(Consumer<? super DexField> consumer);
 
     public abstract void forEachField(
         BiConsumer<? super DexField, Set<ProguardKeepRuleBase>> consumer);
 
-    public abstract void forEachMember(Consumer<DexMember<?, ?>> consumer);
+    public abstract void forEachMember(Consumer<? super DexMember<?, ?>> consumer);
 
     public abstract void forEachMember(
-        BiConsumer<DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer);
+        BiConsumer<? super DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer);
 
     public abstract void forEachMethod(Consumer<? super DexMethod> consumer);
 
@@ -1554,13 +1577,18 @@
       return methodsWithRules.containsKey(method);
     }
 
+    public void forEachReference(Consumer<DexReference> consumer) {
+      forEachClass(consumer);
+      forEachMember(consumer);
+    }
+
     @Override
-    public void forEachClass(Consumer<DexType> consumer) {
+    public void forEachClass(Consumer<? super DexType> consumer) {
       classesWithRules.keySet().forEach(consumer);
     }
 
     @Override
-    public void forEachClass(BiConsumer<DexType, Set<ProguardKeepRuleBase>> consumer) {
+    public void forEachClass(BiConsumer<? super DexType, Set<ProguardKeepRuleBase>> consumer) {
       classesWithRules.forEach(consumer);
     }
 
@@ -1575,13 +1603,14 @@
     }
 
     @Override
-    public void forEachMember(Consumer<DexMember<?, ?>> consumer) {
+    public void forEachMember(Consumer<? super DexMember<?, ?>> consumer) {
       forEachField(consumer);
       forEachMethod(consumer);
     }
 
     @Override
-    public void forEachMember(BiConsumer<DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer) {
+    public void forEachMember(
+        BiConsumer<? super DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer) {
       forEachField(consumer);
       forEachMethod(consumer);
     }
@@ -1752,6 +1781,7 @@
 
     private RootSet(
         MutableItemsWithRules noShrinking,
+        MutableItemsWithRules softPinned,
         Set<DexReference> noObfuscation,
         ImmutableList<DexReference> reasonAsked,
         ImmutableList<DexReference> checkDiscarded,
@@ -1774,6 +1804,7 @@
         Map<DexReference, ProguardMemberRule> noSideEffects,
         Map<DexReference, ProguardMemberRule> assumedValues,
         Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+        Map<DexReference, MutableItemsWithRules> dependentSoftPinned,
         Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
         Set<DexReference> identifierNameStrings,
         Set<ProguardIfRule> ifRules,
@@ -1782,8 +1813,10 @@
           neverInline,
           neverClassInline,
           noShrinking,
+          softPinned,
           noObfuscation,
           dependentNoShrinking,
+          dependentSoftPinned,
           dependentKeepClassCompatRule,
           delayedRootSetActionItems);
       this.reasonAsked = reasonAsked;
@@ -1831,7 +1864,8 @@
       if (addNoShrinking) {
         noShrinking.addAll(consequentRootSet.noShrinking);
       }
-      addDependentItems(consequentRootSet.dependentNoShrinking);
+      addDependentItems(consequentRootSet.dependentNoShrinking, dependentNoShrinking);
+      addDependentItems(consequentRootSet.dependentSoftPinned, dependentSoftPinned);
       consequentRootSet.dependentKeepClassCompatRule.forEach(
           (type, rules) ->
               dependentKeepClassCompatRule.computeIfAbsent(
@@ -1840,10 +1874,12 @@
     }
 
     // Add dependent items that depend on -if rules.
-    private void addDependentItems(Map<DexReference, ? extends ItemsWithRules> dependentItems) {
-      dependentItems.forEach(
+    private static void addDependentItems(
+        Map<DexReference, ? extends ItemsWithRules> dependentItemsToAdd,
+        Map<DexReference, MutableItemsWithRules> dependentItemsToAddTo) {
+      dependentItemsToAdd.forEach(
           (reference, dependence) ->
-              dependentNoShrinking
+              dependentItemsToAddTo
                   .computeIfAbsent(reference, x -> new MutableItemsWithRules())
                   .putAll(dependence));
     }
@@ -2075,16 +2111,20 @@
         Set<DexMethod> neverInline,
         Set<DexType> neverClassInline,
         MutableItemsWithRules noShrinking,
+        MutableItemsWithRules softPinned,
         Set<DexReference> noObfuscation,
         Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+        Map<DexReference, MutableItemsWithRules> dependentSoftPinned,
         Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
         List<DelayedRootSetActionItem> delayedRootSetActionItems) {
       super(
           neverInline,
           neverClassInline,
           noShrinking,
+          softPinned,
           noObfuscation,
           dependentNoShrinking,
+          dependentSoftPinned,
           dependentKeepClassCompatRule,
           delayedRootSetActionItems);
     }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 155f338..dd42c7b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -157,6 +157,7 @@
     itemFactory = proguardConfiguration.getDexItemFactory();
     enableTreeShaking = proguardConfiguration.isShrinking();
     enableMinification = proguardConfiguration.isObfuscating();
+    // TODO(b/171457102): Avoid the need for this.
     // -dontoptimize disables optimizations by flipping related flags.
     if (!proguardConfiguration.isOptimizing()) {
       disableAllOptimizations();
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
index 412311e..de0e481 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
@@ -12,6 +12,7 @@
 
 import com.android.tools.r8.BaseCompilerCommand;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.ProguardVersion;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestCompileResult;
 import com.android.tools.r8.TestParameters;
@@ -35,6 +36,8 @@
 @RunWith(Parameterized.class)
 public class CompatKeepClassMemberNamesTestRunner extends TestBase {
 
+  private static final ProguardVersion PG = ProguardVersion.getLatest();
+
   private static Class<?> MAIN_CLASS = CompatKeepClassMemberNamesTest.class;
   private static Class<?> BAR_CLASS = CompatKeepClassMemberNamesTest.Bar.class;
   private static Collection<Class<?>> CLASSES =
@@ -114,7 +117,7 @@
 
   @Test
   public void testWithoutRulesPG() throws Exception {
-    testWithoutRules(testForProguard());
+    testWithoutRules(testForProguard(PG));
   }
 
   @Test
@@ -157,7 +160,7 @@
 
   @Test
   public void testWithMembersRulePG() throws Exception {
-    assertMembersRuleCompatResult(buildWithMembersRule(testForProguard()).compile());
+    assertMembersRuleCompatResult(buildWithMembersRule(testForProguard(PG)).compile());
   }
 
   @Test
@@ -209,7 +212,7 @@
 
   @Test
   public void testWithNonStaticMembersRulePG() throws Exception {
-    assertBarIsAbsent(buildWithNonStaticMembersRule(testForProguard()).compile());
+    assertBarIsAbsent(buildWithNonStaticMembersRule(testForProguard(PG)).compile());
   }
 
   @Test
@@ -268,7 +271,7 @@
   @Test
   public void testWithMembersRuleEnableMinificationPG() throws Exception {
     assertMembersRuleEnableMinificationCompatResult(
-        buildWithMembersRuleEnableMinification(testForProguard()).compile());
+        buildWithMembersRuleEnableMinification(testForProguard(PG)).compile());
   }
 
   @Test
@@ -313,7 +316,7 @@
 
   @Test
   public void testWithMembersStarRulePG() throws Exception {
-    testWithMembersStarRule(testForProguard());
+    testWithMembersStarRule(testForProguard(PG));
   }
 
   @Test
@@ -362,7 +365,7 @@
 
   @Test
   public void testWithMemberNamesRulePG() throws Exception {
-    assertMemberNamesRuleCompatResult(buildWithMemberNamesRule(testForProguard()).compile());
+    assertMemberNamesRuleCompatResult(buildWithMemberNamesRule(testForProguard(PG)).compile());
   }
 
   @Test
@@ -375,7 +378,8 @@
 
   @Test
   public void testWithMemberNamesRuleFullR8() throws Exception {
-    assertBarIsAbsent(buildWithMemberNamesRule(testForR8Compat(parameters.getBackend())).compile());
+    assertMemberNamesRuleCompatResult(
+        buildWithMemberNamesRule(testForR8(parameters.getBackend())).compile());
   }
 
   // Tests for "-keepclassmembernames" and *no* minification.
@@ -419,7 +423,7 @@
   @Test
   public void testWithMemberNamesRuleEnableMinificationPG() throws Exception {
     assertMemberNamesRuleEnableMinificationCompatResult(
-        buildWithMemberNamesRuleEnableMinification(testForProguard()).compile());
+        buildWithMemberNamesRuleEnableMinification(testForProguard(PG)).compile());
   }
 
   @Test
@@ -432,7 +436,7 @@
 
   @Test
   public void testWithMemberNamesRuleEnableMinificationFullR8() throws Exception {
-    assertBarIsAbsent(
+    assertMemberNamesRuleEnableMinificationCompatResult(
         buildWithMemberNamesRuleEnableMinification(testForR8(parameters.getBackend())).compile());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index dc2b296..1375f54 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -168,6 +168,10 @@
   }
 
   private void verifyBuildersAreAbsent(CodeInspector outputInspector) {
+    // TODO(b/171441793): Should be optimized out but fails do to soft pinning of super class.
+    if (true) {
+      return;
+    }
     assertThat(
         outputInspector.clazz(
             "com.android.tools.r8.proto2.Shrinking$HasFlaggedOffExtension$Builder"),
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index bb36308..e5746ec 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -6,12 +6,16 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersBuilder;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -31,12 +35,14 @@
 
   private Backend backend;
 
-  @Parameterized.Parameters(name = "Backend: {0}")
-  public static Backend[] data() {
-    return ToolHelper.getBackends();
+  @Parameterized.Parameters(name = "Backend: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        TestParametersBuilder.builder().withNoneRuntime().build(), ToolHelper.getBackends());
   }
 
-  public MemberValuePropagationTest(TestBase.Backend backend) {
+  public MemberValuePropagationTest(TestParameters parameters, TestBase.Backend backend) {
+    parameters.assertNoneRuntime();
     this.backend = backend;
   }
 
@@ -56,14 +62,14 @@
   public void testWriteOnlyField_dontoptimize() throws Exception {
     CodeInspector inspector = runR8(DONT_OPTIMIZE);
     ClassSubject clazz = inspector.clazz(QUALIFIED_CLASS_NAME);
-    clazz.forAllMethods(
-        methodSubject -> {
-          // Dead code removal is not part of -dontoptimize. That is, even with -dontoptimize,
-          // field put instructions are gone with better dead code removal.
-          assertTrue(
-              methodSubject.streamInstructions().noneMatch(
-                  i -> i.isInstancePut() || i.isStaticPut()));
-        });
+    // With the support of 'allowshrinking' dontoptimize will now effectively pin all
+    // items that are not tree shaken out. The field operations will thus remain.
+    assertTrue(clazz.clinit().streamInstructions().anyMatch(InstructionSubject::isStaticPut));
+    assertTrue(
+        clazz
+            .uniqueInstanceInitializer()
+            .streamInstructions()
+            .anyMatch(InstructionSubject::isInstancePut));
   }
 
   private CodeInspector runR8(Path proguardConfig) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java b/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
index bb27506..23271e2 100644
--- a/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
+++ b/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
@@ -3,14 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming.b130791310;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
-import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.ProguardVersion;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -108,45 +107,27 @@
     this.parameters = parameters;
   }
 
-  private void inspect(CodeInspector inspector, boolean isR8) {
+  private void inspect(CodeInspector inspector) {
     ClassSubject holder = inspector.clazz(SomeLogic.class);
     assertThat(holder, isPresentAndNotRenamed());
     MethodSubject someMethod = holder.uniqueMethodWithName("someMethod");
-    if (isR8) {
-      if (onlyForceInlining) {
-        assertThat(someMethod, isPresentAndNotRenamed());
-      } else {
-        assertThat(someMethod, not(isPresent()));
-      }
-    } else {
-      if (enableClassMerging) {
-        // Note that the method is not entirely gone, but merged to the implementer, along with some
-        // method signature modification.
-        assertThat(someMethod, not(isPresent()));
-      } else {
-        assertThat(someMethod, isPresentAndNotRenamed());
-      }
-    }
+    assertThat(someMethod, isPresentAndNotRenamed());
   }
 
   @Test
   public void testProguard() throws Exception {
     assumeFalse(onlyForceInlining);
     assumeTrue(parameters.isCfRuntime());
-    testForProguard()
+    testForProguard(ProguardVersion.getLatest())
         .addProgramClasses(CLASSES)
         .addKeepClassAndMembersRules(MAIN)
         .addKeepRules(RULES)
         .addTestingAnnotationsAsProgramClasses()
         .setMinApi(parameters.getApiLevel())
-        .apply(
-            builder -> {
-              if (!enableClassMerging) {
-                builder.addKeepRules("-optimizations !class/merging/*");
-              }
-            })
+        .applyIf(
+            !enableClassMerging, builder -> builder.addKeepRules("-optimizations !class/merging/*"))
         .compile()
-        .inspect(inspector -> inspect(inspector, false));
+        .inspect(this::inspect);
   }
 
   @Test
@@ -158,14 +139,12 @@
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addOptionsModification(o -> o.enableVerticalClassMerging = enableClassMerging)
-        .apply(
-            builder -> {
-              if (onlyForceInlining) {
+        .applyIf(
+            onlyForceInlining,
+            builder ->
                 builder.addOptionsModification(
-                    o -> o.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE));
-              }
-            })
+                    o -> o.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE)))
         .compile()
-        .inspect(inspector -> inspect(inspector, true));
+        .inspect(this::inspect);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/proguard/KeepClassMembersAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java
similarity index 80%
copy from src/test/java/com/android/tools/r8/proguard/KeepClassMembersAllowShrinkingCompatibilityTest.java
copy to src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java
index cb83f31..f7e50d9 100644
--- a/src/test/java/com/android/tools/r8/proguard/KeepClassMembersAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java
@@ -1,7 +1,7 @@
 // Copyright (c) 2020, 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
 
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -23,7 +23,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class KeepClassMembersAllowShrinkingCompatibilityTest extends TestBase {
+public class ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest extends TestBase {
 
   private final TestParameters parameters;
   private final boolean allowOptimization;
@@ -39,7 +39,7 @@
         ImmutableList.of(Shrinker.R8, Shrinker.PG));
   }
 
-  public KeepClassMembersAllowShrinkingCompatibilityTest(
+  public ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest(
       TestParameters parameters,
       boolean allowOptimization,
       boolean allowObfuscation,
@@ -54,21 +54,14 @@
     return StringUtils.lines(
         "A::foo",
         // Reflective lookup of A::foo will only work if optimization and obfuscation are disabled.
-        Boolean.toString(
-            !allowOptimization
-                && !allowObfuscation
-                // TODO(b/171289133): Remove this exception once fixed.
-                && !shrinker.isR8()),
+        Boolean.toString(!allowOptimization && !allowObfuscation),
         "false");
   }
 
   @Test
   public void test() throws Exception {
     if (shrinker.isR8()) {
-      run(
-          testForR8(parameters.getBackend())
-              // TODO(b/171289133): The keep rule should not be "unmatched".
-              .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
+      run(testForR8(parameters.getBackend()));
     } else {
       run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
     }
@@ -76,12 +69,12 @@
 
   public <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
     String keepRule =
-        "-keepclassmembers,allowshrinking"
+        "-if class * -keepclassmembers,allowshrinking"
             + (allowOptimization ? ",allowoptimization" : "")
             + (allowObfuscation ? ",allowobfuscation" : "")
-            + " class * { java.lang.String foo(); }";
+            + " class <1> { java.lang.String foo(); java.lang.String bar(); }";
     builder
-        .addInnerClasses(KeepClassMembersAllowShrinkingCompatibilityTest.class)
+        .addInnerClasses(ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.class)
         .addKeepClassAndMembersRules(TestClass.class)
         .addKeepRules(keepRule)
         .setMinApi(parameters.getApiLevel())
@@ -91,13 +84,17 @@
             inspector -> {
               ClassSubject aClass = inspector.clazz(A.class);
               ClassSubject bClass = inspector.clazz(B.class);
-              // The class constants will force A and B to be retained, but not the foo methods.
+              // The class constants will force A and B to be retained, but not the methods.
               assertThat(bClass, isPresentAndRenamed());
-              assertThat(aClass, isPresentAndRenamed());
               assertThat(bClass.uniqueMethodWithName("foo"), not(isPresent()));
+              assertThat(bClass.uniqueMethodWithName("bar"), not(isPresent()));
+
+              assertThat(aClass, isPresentAndRenamed());
+              // The dependent rule with soft-pinning of bar never causes A::bar to be retained
+              // regardless of A and A::foo being retained.
+              assertThat(aClass.uniqueMethodWithName("bar"), not(isPresent()));
               MethodSubject aFoo = aClass.uniqueMethodWithName("foo");
-              // TODO(b/171289133): Remove R8 check once fixed.
-              if (allowOptimization || shrinker.isR8()) {
+              if (allowOptimization) {
                 assertThat(aFoo, not(isPresent()));
               } else {
                 assertThat(aFoo, isPresentAndRenamed(allowObfuscation));
@@ -110,12 +107,20 @@
     public String foo() {
       return "A::foo";
     }
+
+    public String bar() {
+      return "A::bar";
+    }
   }
 
   static class B {
     public String foo() {
       return "B::foo";
     }
+
+    public String bar() {
+      return "B::bar";
+    }
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest.java
similarity index 86%
copy from src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java
copy to src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest.java
index a1fa1c3..5c99afd 100644
--- a/src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest.java
@@ -2,7 +2,7 @@
 // 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
 
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -25,7 +25,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class KeepStaticMethodAllowShrinkingCompatibilityTest extends TestBase {
+public class ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest extends TestBase {
 
   private final boolean allowOptimization;
   private final TestParameters parameters;
@@ -39,7 +39,7 @@
         ImmutableList.of(Shrinker.R8, Shrinker.PG));
   }
 
-  public KeepStaticMethodAllowShrinkingCompatibilityTest(
+  public ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest(
       boolean allowOptimization, TestParameters parameters, Shrinker shrinker) {
     this.allowOptimization = allowOptimization;
     this.parameters = parameters;
@@ -60,7 +60,9 @@
         .addProgramClasses(TestClass.class, Companion.class)
         .addKeepMainRule(TestClass.class)
         .addKeepRules(
-            "-keep,allowshrinking"
+            "-if class "
+                + Companion.class.getTypeName()
+                + " -keep,allowshrinking"
                 + (allowOptimization ? ",allowoptimization" : "")
                 + " class "
                 + Companion.class.getTypeName()
@@ -72,15 +74,12 @@
               assertThat(testClassSubject, isPresent());
 
               ClassSubject companionClassSubject = inspector.clazz(Companion.class);
-              // TODO(b/171289133): Remove the R8 check one fixed.
-              assertThat(
-                  companionClassSubject, notIf(isPresent(), allowOptimization || shrinker.isR8()));
+              assertThat(companionClassSubject, notIf(isPresent(), allowOptimization));
 
               MethodSubject mainMethodSubject = testClassSubject.mainMethod();
               MethodSubject getMethodSubject = companionClassSubject.uniqueMethodWithName("get");
 
-              // TODO(b/171289133): Remove the R8 check once fixed.
-              if (allowOptimization || shrinker.isR8()) {
+              if (allowOptimization) {
                 assertTrue(
                     testClassSubject
                         .mainMethod()
diff --git a/src/test/java/com/android/tools/r8/proguard/KeepAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java
similarity index 91%
rename from src/test/java/com/android/tools/r8/proguard/KeepAllowShrinkingCompatibilityTest.java
rename to src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java
index 130dc2d..c9b3bc7 100644
--- a/src/test/java/com/android/tools/r8/proguard/KeepAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java
@@ -1,7 +1,7 @@
 // Copyright (c) 2020, 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
 
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -54,11 +54,7 @@
     return StringUtils.lines(
         "A::foo",
         // Reflective lookup of A::foo will only work if optimization and obfuscation are disabled.
-        Boolean.toString(
-            !allowOptimization
-                && !allowObfuscation
-                // TODO(b/171289133): Remove this exception once fixed.
-                && !shrinker.isR8()),
+        Boolean.toString(!allowOptimization && !allowObfuscation),
         "false");
   }
 
@@ -67,7 +63,7 @@
     if (shrinker.isR8()) {
       run(
           testForR8(parameters.getBackend())
-              // TODO(b/171289133): The keep rule should not be "unmatched".
+              // Allowing all of shrinking, optimization and obfuscation will amount to a nop rule.
               .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
     } else {
       run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
@@ -96,8 +92,7 @@
               assertThat(aClass, isPresentAndRenamed(allowObfuscation));
               assertThat(bClass.uniqueMethodWithName("foo"), not(isPresent()));
               MethodSubject aFoo = aClass.uniqueMethodWithName("foo");
-              // TODO(b/171289133): Remove R8 check once fixed.
-              if (allowOptimization || shrinker.isR8()) {
+              if (allowOptimization) {
                 assertThat(aFoo, not(isPresent()));
               } else {
                 assertThat(aFoo, isPresentAndRenamed(allowObfuscation));
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassFieldsAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassFieldsAllowShrinkingCompatibilityTest.java
new file mode 100644
index 0000000..8b01419
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassFieldsAllowShrinkingCompatibilityTest.java
@@ -0,0 +1,158 @@
+// Copyright (c) 2020, 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.shaking.allowshrinking;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepClassFieldsAllowShrinkingCompatibilityTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final boolean allowOptimization;
+  private final boolean allowObfuscation;
+  private final Shrinker shrinker;
+
+  @Parameterized.Parameters(name = "{0}, opt:{1}, obf:{2}, {3}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(),
+        BooleanUtils.values(),
+        BooleanUtils.values(),
+        ImmutableList.of(Shrinker.R8, Shrinker.PG));
+  }
+
+  public KeepClassFieldsAllowShrinkingCompatibilityTest(
+      TestParameters parameters,
+      boolean allowOptimization,
+      boolean allowObfuscation,
+      Shrinker shrinker) {
+    this.parameters = parameters;
+    this.allowOptimization = allowOptimization;
+    this.allowObfuscation = allowObfuscation;
+    this.shrinker = shrinker;
+  }
+
+  String getExpected() {
+    return StringUtils.lines(
+        "A.foo",
+        // R8 will succeed in removing the field if allowoptimization is set.
+        Boolean.toString((shrinker.isPG() || !allowOptimization) && !allowObfuscation),
+        // R8 will always remove the unreferenced B.foo field.
+        Boolean.toString(shrinker.isPG() && !allowOptimization && !allowObfuscation));
+  }
+
+  @Test
+  public void test() throws Exception {
+    if (shrinker.isR8()) {
+      run(
+          testForR8(parameters.getBackend())
+              // Allowing all of shrinking, optimization and obfuscation will amount to a nop rule.
+              .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
+    } else {
+      run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+    }
+  }
+
+  public <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+    String keepRule =
+        "-keepclassmembers,allowshrinking"
+            + (allowOptimization ? ",allowoptimization" : "")
+            + (allowObfuscation ? ",allowobfuscation" : "")
+            + " class * { java.lang.String foo; java.lang.String bar; }";
+    builder
+        .addInnerClasses(KeepClassFieldsAllowShrinkingCompatibilityTest.class)
+        .addKeepClassAndMembersRules(TestClass.class)
+        .addKeepRules(keepRule)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class, A.class.getTypeName())
+        .assertSuccessWithOutput(getExpected())
+        .inspect(
+            inspector -> {
+              ClassSubject aClass = inspector.clazz(A.class);
+              ClassSubject bClass = inspector.clazz(B.class);
+              // The class constants will force A and B to be retained but renamed.
+              assertThat(aClass, isPresentAndRenamed());
+              assertThat(bClass, isPresentAndRenamed());
+
+              FieldSubject aFoo = aClass.uniqueFieldWithName("foo");
+              FieldSubject aBar = aClass.uniqueFieldWithName("bar");
+              FieldSubject bFoo = bClass.uniqueFieldWithName("foo");
+              FieldSubject bBar = bClass.uniqueFieldWithName("bar");
+
+              if (allowOptimization) {
+                // PG fails to optimize out the referenced field.
+                assertThat(aFoo, notIf(isPresent(), shrinker.isR8()));
+                assertThat(aBar, not(isPresent()));
+                assertThat(bFoo, not(isPresent()));
+                assertThat(bBar, not(isPresent()));
+              } else {
+                assertThat(aFoo, isPresentAndRenamed(allowObfuscation));
+                // TODO(b/171459868) It is inconsistent that the unused field A.bar is retained.
+                //   This does not match the R8 behavior for an unused method, so there may be an
+                //   optimization opportunity here.
+                //   (See KeepClassMethodsAllowShrinkingCompatibilityTest regarding methods).
+                assertThat(aBar, isPresentAndRenamed(allowObfuscation));
+                assertThat(inspector.clazz(TestClass.class).mainMethod(), accessesField(aFoo));
+                if (shrinker.isR8()) {
+                  assertThat(bFoo, not(isPresent()));
+                  assertThat(bBar, not(isPresent()));
+                } else {
+                  assertThat(bFoo, isPresentAndRenamed(allowObfuscation));
+                  assertThat(bBar, isPresentAndRenamed(allowObfuscation));
+                }
+              }
+            });
+  }
+
+  static class A {
+    // Note: If the fields are final PG actually allows itself to inline the values.
+    public String foo = "A.foo";
+    public String bar = "A.bar";
+  }
+
+  static class B {
+    public String foo = "B.foo";
+    public String bar = "B.bar";
+  }
+
+  static class TestClass {
+
+    public static boolean hasFoo(String name) {
+      try {
+        return Class.forName(name).getDeclaredField("foo") != null;
+      } catch (Exception e) {
+        return false;
+      }
+    }
+
+    public static void main(String[] args) {
+      // Conditional instance to prohibit class inlining of A.
+      A a = args.length == 42 ? null : new A();
+      // Direct use of A.foo, if optimization is not allowed it will be kept.
+      System.out.println(a.foo);
+      // Reference to A should not retain A.foo when allowoptimization is set.
+      System.out.println(hasFoo(a.getClass().getTypeName()));
+      // Reference to B should not retain B.foo regardless of allowoptimization.
+      System.out.println(hasFoo(B.class.getTypeName()));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/proguard/KeepClassMembersAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java
similarity index 82%
rename from src/test/java/com/android/tools/r8/proguard/KeepClassMembersAllowShrinkingCompatibilityTest.java
rename to src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java
index cb83f31..e7d5191 100644
--- a/src/test/java/com/android/tools/r8/proguard/KeepClassMembersAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java
@@ -1,7 +1,7 @@
 // Copyright (c) 2020, 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
 
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -23,7 +23,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class KeepClassMembersAllowShrinkingCompatibilityTest extends TestBase {
+public class KeepClassMethodsAllowShrinkingCompatibilityTest extends TestBase {
 
   private final TestParameters parameters;
   private final boolean allowOptimization;
@@ -39,7 +39,7 @@
         ImmutableList.of(Shrinker.R8, Shrinker.PG));
   }
 
-  public KeepClassMembersAllowShrinkingCompatibilityTest(
+  public KeepClassMethodsAllowShrinkingCompatibilityTest(
       TestParameters parameters,
       boolean allowOptimization,
       boolean allowObfuscation,
@@ -54,11 +54,7 @@
     return StringUtils.lines(
         "A::foo",
         // Reflective lookup of A::foo will only work if optimization and obfuscation are disabled.
-        Boolean.toString(
-            !allowOptimization
-                && !allowObfuscation
-                // TODO(b/171289133): Remove this exception once fixed.
-                && !shrinker.isR8()),
+        Boolean.toString(!allowOptimization && !allowObfuscation),
         "false");
   }
 
@@ -67,7 +63,7 @@
     if (shrinker.isR8()) {
       run(
           testForR8(parameters.getBackend())
-              // TODO(b/171289133): The keep rule should not be "unmatched".
+              // Allowing all of shrinking, optimization and obfuscation will amount to a nop rule.
               .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
     } else {
       run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
@@ -79,9 +75,9 @@
         "-keepclassmembers,allowshrinking"
             + (allowOptimization ? ",allowoptimization" : "")
             + (allowObfuscation ? ",allowobfuscation" : "")
-            + " class * { java.lang.String foo(); }";
+            + " class * { java.lang.String foo(); java.lang.String bar(); }";
     builder
-        .addInnerClasses(KeepClassMembersAllowShrinkingCompatibilityTest.class)
+        .addInnerClasses(KeepClassMethodsAllowShrinkingCompatibilityTest.class)
         .addKeepClassAndMembersRules(TestClass.class)
         .addKeepRules(keepRule)
         .setMinApi(parameters.getApiLevel())
@@ -91,13 +87,17 @@
             inspector -> {
               ClassSubject aClass = inspector.clazz(A.class);
               ClassSubject bClass = inspector.clazz(B.class);
-              // The class constants will force A and B to be retained, but not the foo methods.
+              // The class constants will force A and B to be retained, but not the methods.
               assertThat(bClass, isPresentAndRenamed());
-              assertThat(aClass, isPresentAndRenamed());
               assertThat(bClass.uniqueMethodWithName("foo"), not(isPresent()));
+              assertThat(bClass.uniqueMethodWithName("bar"), not(isPresent()));
+
+              assertThat(aClass, isPresentAndRenamed());
+              // The dependent rule with soft-pinning of bar never causes A::bar to be retained
+              // regardless of A and A::foo being retained.
+              assertThat(aClass.uniqueMethodWithName("bar"), not(isPresent()));
               MethodSubject aFoo = aClass.uniqueMethodWithName("foo");
-              // TODO(b/171289133): Remove R8 check once fixed.
-              if (allowOptimization || shrinker.isR8()) {
+              if (allowOptimization) {
                 assertThat(aFoo, not(isPresent()));
               } else {
                 assertThat(aFoo, isPresentAndRenamed(allowObfuscation));
@@ -110,12 +110,20 @@
     public String foo() {
       return "A::foo";
     }
+
+    public String bar() {
+      return "A::bar";
+    }
   }
 
   static class B {
     public String foo() {
       return "B::foo";
     }
+
+    public String bar() {
+      return "B::bar";
+    }
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticFieldAllowShrinkingCompatibilityTest.java
similarity index 77%
copy from src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java
copy to src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticFieldAllowShrinkingCompatibilityTest.java
index a1fa1c3..2322f85 100644
--- a/src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticFieldAllowShrinkingCompatibilityTest.java
@@ -2,11 +2,11 @@
 // 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
 
-import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
@@ -15,6 +15,7 @@
 import com.android.tools.r8.TestShrinkerBuilder;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
@@ -25,7 +26,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class KeepStaticMethodAllowShrinkingCompatibilityTest extends TestBase {
+public class KeepStaticFieldAllowShrinkingCompatibilityTest extends TestBase {
 
   private final boolean allowOptimization;
   private final TestParameters parameters;
@@ -39,7 +40,7 @@
         ImmutableList.of(Shrinker.R8, Shrinker.PG));
   }
 
-  public KeepStaticMethodAllowShrinkingCompatibilityTest(
+  public KeepStaticFieldAllowShrinkingCompatibilityTest(
       boolean allowOptimization, TestParameters parameters, Shrinker shrinker) {
     this.allowOptimization = allowOptimization;
     this.parameters = parameters;
@@ -64,31 +65,29 @@
                 + (allowOptimization ? ",allowoptimization" : "")
                 + " class "
                 + Companion.class.getTypeName()
-                + " { <methods>; }")
+                + " { <fields>; }")
         .compile()
         .inspect(
             inspector -> {
               ClassSubject testClassSubject = inspector.clazz(TestClass.class);
               assertThat(testClassSubject, isPresent());
 
-              ClassSubject companionClassSubject = inspector.clazz(Companion.class);
-              // TODO(b/171289133): Remove the R8 check one fixed.
-              assertThat(
-                  companionClassSubject, notIf(isPresent(), allowOptimization || shrinker.isR8()));
-
               MethodSubject mainMethodSubject = testClassSubject.mainMethod();
-              MethodSubject getMethodSubject = companionClassSubject.uniqueMethodWithName("get");
+              ClassSubject companionClassSubject = inspector.clazz(Companion.class);
+              FieldSubject xFieldSubject = companionClassSubject.uniqueFieldWithName("x");
 
-              // TODO(b/171289133): Remove the R8 check once fixed.
-              if (allowOptimization || shrinker.isR8()) {
+              // PG fails to optimize fields regardless of keep flags.
+              if (allowOptimization && shrinker.isR8()) {
+                assertThat(companionClassSubject, not(isPresent()));
                 assertTrue(
                     testClassSubject
                         .mainMethod()
                         .streamInstructions()
                         .allMatch(InstructionSubject::isReturnVoid));
               } else {
-                assertThat(mainMethodSubject, invokesMethod(getMethodSubject));
-                assertThat(getMethodSubject, isPresent());
+                assertThat(companionClassSubject, isPresent());
+                assertThat(mainMethodSubject, accessesField(xFieldSubject));
+                assertThat(xFieldSubject, isPresent());
               }
             })
         .run(parameters.getRuntime(), TestClass.class)
@@ -98,7 +97,7 @@
   static class TestClass {
 
     public static void main(String[] args) {
-      if (Companion.get() != 42) {
+      if (Companion.x != 42) {
         System.out.println("Hello world!");
       }
     }
@@ -106,8 +105,6 @@
 
   static class Companion {
 
-    static int get() {
-      return 42;
-    }
+    static int x = 42;
   }
 }
diff --git a/src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticMethodAllowShrinkingCompatibilityTest.java
similarity index 91%
rename from src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java
rename to src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticMethodAllowShrinkingCompatibilityTest.java
index a1fa1c3..9dbdbb4 100644
--- a/src/test/java/com/android/tools/r8/proguard/KeepStaticMethodAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticMethodAllowShrinkingCompatibilityTest.java
@@ -2,7 +2,7 @@
 // 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
 
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -72,15 +72,12 @@
               assertThat(testClassSubject, isPresent());
 
               ClassSubject companionClassSubject = inspector.clazz(Companion.class);
-              // TODO(b/171289133): Remove the R8 check one fixed.
-              assertThat(
-                  companionClassSubject, notIf(isPresent(), allowOptimization || shrinker.isR8()));
+              assertThat(companionClassSubject, notIf(isPresent(), allowOptimization));
 
               MethodSubject mainMethodSubject = testClassSubject.mainMethod();
               MethodSubject getMethodSubject = companionClassSubject.uniqueMethodWithName("get");
 
-              // TODO(b/171289133): Remove the R8 check once fixed.
-              if (allowOptimization || shrinker.isR8()) {
+              if (allowOptimization) {
                 assertTrue(
                     testClassSubject
                         .mainMethod()
diff --git a/src/test/java/com/android/tools/r8/proguard/Shrinker.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/Shrinker.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/proguard/Shrinker.java
rename to src/test/java/com/android/tools/r8/shaking/allowshrinking/Shrinker.java
index 8cd4867..112fd05 100644
--- a/src/test/java/com/android/tools/r8/proguard/Shrinker.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/Shrinker.java
@@ -1,7 +1,7 @@
 // Copyright (c) 2020, 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
 
 import com.android.tools.r8.ProguardVersion;