Don't add keep info to items until they become live

Change-Id: Id717c915f99519e4bc9a2e12dcccc4a4bbc63ddf
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 ecc5194..c93bdb3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -944,7 +944,10 @@
     workList.enqueueMarkFieldKeptAction(
         field,
         graphReporter.reportKeepField(
-            precondition, minimumKeepInfo.getRules(), field.getDefinition()));
+            precondition,
+            minimumKeepInfo.getReasons(),
+            minimumKeepInfo.getRules(),
+            field.getDefinition()));
   }
 
   private void enqueueMethodDueToNoShrinkingRule(
@@ -3543,7 +3546,7 @@
               (clazz, minimumKeepInfo) ->
                   applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfo),
               (field, minimumKeepInfo) ->
-                  applyMinimumKeepInfoWhenLive(field, preconditionEvent, minimumKeepInfo),
+                  applyMinimumKeepInfoWhenLive(field, minimumKeepInfo, preconditionEvent),
               this::applyMinimumKeepInfoWhenLiveOrTargeted);
     }
     enqueueAllIfNotShrinking();
@@ -3637,7 +3640,12 @@
   }
 
   private void applyMinimumKeepInfoWhenLive(
-      ProgramField field, EnqueuerEvent preconditionEvent, KeepFieldInfo.Joiner minimumKeepInfo) {
+      ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo) {
+    applyMinimumKeepInfoWhenLive(field, minimumKeepInfo, EnqueuerEvent.unconditional());
+  }
+
+  private void applyMinimumKeepInfoWhenLive(
+      ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo, EnqueuerEvent preconditionEvent) {
     if (liveFields.contains(field)) {
       keepInfo.joinField(field, info -> info.merge(minimumKeepInfo));
     } else {
@@ -3660,7 +3668,7 @@
   private void recordDependentMinimumKeepInfo(
       EnqueuerEvent preconditionEvent, ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo) {
     if (isPreconditionForMinimumKeepInfoSatisfied(preconditionEvent)) {
-      applyMinimumKeepInfoWhenLive(field, preconditionEvent, minimumKeepInfo);
+      applyMinimumKeepInfoWhenLive(field, minimumKeepInfo, preconditionEvent);
     } else {
       dependentMinimumKeepInfo
           .getOrCreateMinimumKeepInfoFor(preconditionEvent)
@@ -3741,7 +3749,7 @@
           (clazz, minimumKeepInfoForClass) ->
               applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfoForClass),
           (field, minimumKeepInfoForField) ->
-              applyMinimumKeepInfoWhenLive(field, preconditionEvent, minimumKeepInfoForField),
+              applyMinimumKeepInfoWhenLive(field, minimumKeepInfoForField, preconditionEvent),
           (method, minimumKeepInfoForMethod) ->
               applyMinimumKeepInfoWhenLiveOrTargeted(
                   method, minimumKeepInfoForMethod, preconditionEvent));
@@ -4841,9 +4849,12 @@
       }
       if (keepInfo.getFieldInfo(encodedField, clazz).isShrinkingAllowed(options)) {
         ProgramField programField = new ProgramField(clazz, encodedField);
-        keepInfo.joinField(
-            programField, joiner -> joiner.disallowOptimization().disallowShrinking());
-        markFieldAsKept(programField, KeepReason.reflectiveUseIn(method));
+        applyMinimumKeepInfoWhenLive(
+            programField,
+            KeepFieldInfo.newEmptyJoiner()
+                .disallowOptimization()
+                .disallowShrinking()
+                .addReason(KeepReason.reflectiveUseIn(method)));
       }
     } else {
       assert referencedItem.isDexMethod();
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index 2fe6b81..c25095b 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -162,9 +162,15 @@
   }
 
   KeepReasonWitness reportKeepField(
-      DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedField field) {
-    assert !rules.isEmpty() || !options.isShrinking();
+      DexDefinition precondition,
+      Collection<KeepReason> reasons,
+      Collection<ProguardKeepRuleBase> rules,
+      DexEncodedField field) {
+    assert !reasons.isEmpty() || !rules.isEmpty() || !options.isShrinking();
     if (keptGraphConsumer != null) {
+      for (KeepReason reason : reasons) {
+        registerField(field, reason);
+      }
       for (ProguardKeepRuleBase rule : rules) {
         reportKeepField(precondition, rule, field);
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index b45eb72..f85dc0f 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.shaking.KeepInfo.Builder;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Consumer;
 
@@ -477,6 +478,7 @@
     // The set of rules that have contributed to this joiner. These are only needed for the
     // interpretation of keep rules into keep info, and is therefore not stored in the keep info
     // builder above.
+    final Set<KeepReason> reasons = new HashSet<>();
     final Set<ProguardKeepRuleBase> rules = Sets.newIdentityHashSet();
 
     Joiner(B builder) {
@@ -506,6 +508,10 @@
       return null;
     }
 
+    public Set<KeepReason> getReasons() {
+      return reasons;
+    }
+
     public Set<ProguardKeepRuleBase> getRules() {
       return rules;
     }
@@ -535,6 +541,11 @@
       return self();
     }
 
+    public J addReason(KeepReason reason) {
+      reasons.add(reason);
+      return self();
+    }
+
     public J addRule(ProguardKeepRuleBase rule) {
       rules.add(rule);
       return self();
@@ -592,6 +603,7 @@
       applyIf(
           builder.isAccessModificationRequiredForRepackaging(),
           Joiner::requireAccessModificationForRepackaging);
+      reasons.addAll(joiner.reasons);
       rules.addAll(joiner.rules);
       return self();
     }
@@ -610,7 +622,7 @@
 
     public boolean verifyShrinkingDisallowedWithRule(InternalOptions options) {
       assert !isShrinkingAllowed();
-      assert !getRules().isEmpty() || !options.isShrinking();
+      assert !getReasons().isEmpty() || !getRules().isEmpty() || !options.isShrinking();
       return true;
     }
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/reflection/FieldAccessTest.java b/src/test/java/com/android/tools/r8/shaking/reflection/FieldAccessTest.java
index dd8dd90..9d2b858 100644
--- a/src/test/java/com/android/tools/r8/shaking/reflection/FieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/reflection/FieldAccessTest.java
@@ -8,7 +8,8 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
@@ -18,20 +19,18 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
 public class FieldAccessTest extends TestBase {
 
-  private final Backend backend;
+  @Parameter(0)
+  public TestParameters parameters;
 
   @Parameters(name = "{0}")
-  public static Backend[] data() {
-    return ToolHelper.getBackends();
-  }
-
-  public FieldAccessTest(Backend backend) {
-    this.backend = backend;
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   private boolean isInvokeGetField(InstructionSubject instruction) {
@@ -45,27 +44,30 @@
         methodFromMethod(testClass.getDeclaredMethod("main", String[].class));
     FieldReference fooField = fieldFromField(testClass.getDeclaredField("foo"));
 
-    testForR8(Backend.DEX)
+    testForR8(parameters.getBackend())
         .enableGraphInspector()
         .addProgramClasses(testClass)
         .addKeepMainRule(testClass)
-        .run(testClass)
-        .inspectGraph(inspector -> {
-          // The only root should be the keep annotation rule.
-          assertEquals(1, inspector.getRoots().size());
-          QueryNode root = inspector.rule(Origin.unknown(), 1, 1).assertRoot();
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), testClass)
+        .inspectGraph(
+            inspector -> {
+              // The only root should be the keep annotation rule.
+              assertEquals(1, inspector.getRoots().size());
+              QueryNode root = inspector.rule(Origin.unknown(), 1, 1).assertRoot();
 
-          inspector.method(mainMethod).assertNotRenamed().assertKeptBy(root);
-          inspector.field(fooField).assertRenamed();
-        })
-        .inspect(inspector -> {
-          Assert.assertTrue(
-              inspector
-                  .clazz(testClass)
-                  .uniqueMethodWithName("main")
-                  .streamInstructions()
-                  .anyMatch(this::isInvokeGetField));
-        })
+              inspector.method(mainMethod).assertNotRenamed().assertKeptBy(root);
+              inspector.field(fooField).assertRenamed();
+            })
+        .inspect(
+            inspector -> {
+              Assert.assertTrue(
+                  inspector
+                      .clazz(testClass)
+                      .uniqueMethodWithName("main")
+                      .streamInstructions()
+                      .anyMatch(this::isInvokeGetField));
+            })
         .assertSuccessWithOutput("42");
   }