Read Kotlin @Metadata after 1st tree shaking.

Bug: 70169921, 148654451
Change-Id: I39223a81df9e3ce5098d33be2c06a4698c1ad24c
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7db3d7c..ecf6749 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -306,10 +306,6 @@
           }
         }
 
-        // Compute kotlin info before setting the roots and before
-        // kotlin metadata annotation is removed.
-        computeKotlinInfoForProgramClasses(application, appView);
-
         // Add synthesized -assumenosideeffects from min api if relevant.
         if (options.isGeneratingDex()) {
           if (!ProguardConfigurationUtils.hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
@@ -399,6 +395,22 @@
         appView.appInfo().unsetObsolete();
       }
 
+      // Compute Kotlin info after the first round of tree shaking. With this quasi-lazy loading,
+      // we only read Kotlin @Metadata associated with _live_ program classes.
+      //
+      // One caveat is, Kotlin @Metadata annotation would be freely removed according to general
+      // logic in {@link AnnotationRemover}. To properly parse annotations, one needs a rule like:
+      //   -keep class kotlin.Metadata { *; }
+      // not like:
+      //   -keep class kotlin.Metadata
+      // which specifies to keep Metadata, but its members, e.g., kind, d1, d2, etc., are still
+      // allowed to be shrunken.
+      //
+      // We can't postpone it further, though, since the following structural optimizations, such
+      // as vertical class merging, can migrate, e.g., methods from one class to its sub-class or
+      // implementer while the corresponding (member-level) @Metadata is not yet read.
+      computeKotlinInfoForProgramClasses(application, appView);
+
       // The class type lattice elements include information about the interfaces that a class
       // implements. This information can change as a result of vertical class merging, so we need
       // to clear the cache, so that we will recompute the type lattice elements.
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
index 6bfafc4..fd89b90 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
@@ -12,6 +12,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
@@ -24,6 +25,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import org.hamcrest.core.StringContains;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -100,8 +102,8 @@
         .addProgramFiles(annoJarMap.get(targetVersion), ToolHelper.getKotlinStdlibJar())
         .addProgramFiles(inputJarMap.get(targetVersion))
         .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT)
-        .addKeepRules("-keep class **.Anno")
-        .addKeepRules("-keep class kotlin.Metadata")
+        .addKeepRules("-keep class **.Anno { *; }")
+        .addKeepRules("-keep class kotlin.Metadata { *; }")
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
         .allowDiagnosticWarningMessages()
         .compile()
@@ -110,6 +112,35 @@
         .inspect(this::inspect);
   }
 
+  @Test
+  public void testR8_kotlinStdlibAsProgramFile_insufficientRule() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(annoJarMap.get(targetVersion), ToolHelper.getKotlinStdlibJar())
+        .addProgramFiles(inputJarMap.get(targetVersion))
+        .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT)
+        .addKeepRules("-keep class **.Anno { *; }")
+        .addKeepRules("-keep class kotlin.Metadata { *** d1; }")
+        .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+        .allowDiagnosticMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertErrorsCount(0);
+              diagnostics.assertInfosCount(4);
+              for (Diagnostic diagnostic : diagnostics.getInfos()) {
+                assertThat(
+                    diagnostic.getDiagnosticMessage(),
+                    StringContains.containsString(
+                        "has malformed kotlin.Metadata: element 'k' is missing."));
+              }
+              diagnostics.assertWarningsCount(3);
+              for (Diagnostic diagnostic : diagnostics.getWarnings()) {
+                assertThat(
+                    diagnostic.getDiagnosticMessage(),
+                    StringContains.containsString("Resource 'META-INF/"));
+              }
+            });
+  }
+
   private void inspect(CodeInspector inspector) {
     String pkg = getClass().getPackage().getName();
     ClassSubject kept = inspector.clazz(pkg + ".anno.Kept");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 31cd07d..2f2d3dc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -51,7 +51,7 @@
             .addProgramFiles(ToolHelper.getKotlinReflectJar())
             .addKeepMainRule(mainClassName)
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .addKeepRules("-keep class kotlin.Metadata")
+            .addKeepRules("-keep class kotlin.Metadata { *; }")
             // TODO(b/70169921): if this option is settled down, this test is meaningless.
             .addOptionsModification(o -> o.enableKotlinMetadataRewritingForRenamedClasses = false)
             .allowDiagnosticWarningMessages()