Only retain non-runtime annotations in intermediate mode.

Bug: 130028992
Change-Id: Idb1fb90c041159d5fa481e75071791b8b6a8e61e
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index f0591ee..9438632 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -249,6 +249,7 @@
     internal.minimalMainDex = internal.debug;
     internal.minApiLevel = getMinApiLevel();
     internal.intermediate = intermediate;
+    internal.readCompileTimeAnnotations = intermediate;
     // Assert and fixup defaults.
     assert !internal.isShrinking();
     assert !internal.isMinifying();
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 2a290b6..168d3e9 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -151,10 +152,31 @@
       List<DexAnnotation> annotations,
       JarApplicationReader application) {
     assert annotations != null;
-    int visiblity = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
-    return new CreateAnnotationVisitor(application, (names, values) ->
-        annotations.add(new DexAnnotation(visiblity,
-            createEncodedAnnotation(desc, names, values, application))));
+    if (visible || retainCompileTimeAnnotation(desc, application)) {
+      int visiblity = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
+      return new CreateAnnotationVisitor(
+          application,
+          (names, values) ->
+              annotations.add(
+                  new DexAnnotation(
+                      visiblity, createEncodedAnnotation(desc, names, values, application))));
+    }
+    return null;
+  }
+
+  private static boolean retainCompileTimeAnnotation(
+      String desc, JarApplicationReader application) {
+    if (application.options.readCompileTimeAnnotations) {
+      return true;
+    }
+    if (application.options.processCovariantReturnTypeAnnotations) {
+      // @CovariantReturnType annotations are processed by CovariantReturnTypeAnnotationTransformer,
+      // they thus need to be read here and will then be removed as part of the processing.
+      DexType type = application.getTypeFromDescriptor(desc);
+      return CovariantReturnTypeAnnotationTransformer.isCovariantReturnTypeAnnotation(
+          type, application.options.itemFactory);
+    }
+    return false;
   }
 
   private static DexEncodedAnnotation createEncodedAnnotation(String desc,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 0c76382..9c0c542 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -248,8 +248,12 @@
   }
 
   private boolean isCovariantReturnTypeAnnotation(DexEncodedAnnotation annotation) {
-    return annotation.type == factory.annotationCovariantReturnType
-        || annotation.type == factory.annotationCovariantReturnTypes;
+    return isCovariantReturnTypeAnnotation(annotation.type, factory);
+  }
+
+  public static boolean isCovariantReturnTypeAnnotation(DexType type, DexItemFactory factory) {
+    return type == factory.annotationCovariantReturnType
+        || type == factory.annotationCovariantReturnTypes;
   }
 
   private static boolean hasVirtualMethodWithSignature(DexClass clazz, DexEncodedMethod method) {
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 d227afd..9d3c521 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -262,6 +262,7 @@
   // Skipping min_api check and compiling an intermediate result intended for later merging.
   // Intermediate builds also emits or update synthesized classes mapping.
   public boolean intermediate = false;
+  public boolean readCompileTimeAnnotations = true;
   public List<String> logArgumentsFilter = ImmutableList.of();
 
   // Flag to turn on/off lambda class merging in R8.
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 11dc790..310124e 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -734,6 +734,13 @@
   // checked into the Art repo.
   private static final Multimap<String, TestCondition> failingRunWithArtOutput =
       new ImmutableListMultimap.Builder<String, TestCondition>()
+          // This test assumes that class-retention annotations are preserved by the compiler and
+          // then checks for backwards compatibility with M where they could incorrectly be observed
+          // by the program at runtime.
+          .put(
+              "005-annotations",
+              TestCondition.match(
+                  TestCondition.compilers(CompilerUnderTest.D8, CompilerUnderTest.D8_AFTER_R8CF)))
           // On Art 4.4.4 we have fewer refs than expected (except for d8 when compiled with dx).
           .put("072-precise-gc",
               TestCondition.match(
diff --git a/src/test/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
index 581bfe8..4f512b7 100644
--- a/src/test/java/com/android/tools/r8/TestBaseBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
@@ -9,9 +9,11 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableMap;
 import java.nio.file.Path;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 public abstract class TestBaseBuilder<
         C extends BaseCommand,
@@ -67,6 +69,16 @@
     return self();
   }
 
+  public T addMainDexListClasses(Class<?>... classes) {
+    return addMainDexListClasses(Arrays.asList(classes));
+  }
+
+  public T addMainDexListClasses(Collection<Class<?>> classes) {
+    builder.addMainDexClasses(
+        classes.stream().map(Class::getTypeName).collect(Collectors.toList()));
+    return self();
+  }
+
   public T addMainDexListFiles(Collection<Path> files) {
     builder.addMainDexListFiles(files);
     return self();
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 42d47e3..6fbb80f 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -23,7 +23,7 @@
       getAvailableRuntimes().collect(Collectors.toList());
 
   // Predicate describing which test parameters are applicable to the test.
-  // Built via the methods found below. Default to no applicable parameters, i.e., the emtpy set.
+  // Built via the methods found below. Defaults to no applicable parameters, i.e., the emtpy set.
   private Predicate<TestParameters> filter = param -> false;
 
   private TestParametersBuilder() {}
@@ -142,11 +142,34 @@
    */
   private static final AndroidApiLevel lowestCompilerApiLevel = AndroidApiLevel.B;
 
-  private boolean enableAllApiLevels = false;
+  private boolean enableApiLevels = false;
+
+  private Predicate<AndroidApiLevel> apiLevelFilter = param -> false;
+
+  private TestParametersBuilder withApiFilter(Predicate<AndroidApiLevel> filter) {
+    enableApiLevels = true;
+    apiLevelFilter = apiLevelFilter.or(filter);
+    return this;
+  }
 
   public TestParametersBuilder withAllApiLevels() {
-    enableAllApiLevels = true;
-    return this;
+    return withApiFilter(api -> true);
+  }
+
+  public TestParametersBuilder withApiLevelsStartingAtIncluding(AndroidApiLevel startInclusive) {
+    return withApiFilter(api -> startInclusive.getLevel() <= api.getLevel());
+  }
+
+  public TestParametersBuilder withApiLevelsStartingAtExcluding(AndroidApiLevel startExclusive) {
+    return withApiFilter(api -> startExclusive.getLevel() < api.getLevel());
+  }
+
+  public TestParametersBuilder withApiLevelsEndingAtInclusive(AndroidApiLevel endInclusive) {
+    return withApiFilter(api -> api.getLevel() <= endInclusive.getLevel());
+  }
+
+  public TestParametersBuilder withApiLevelsEndingAtExcluding(AndroidApiLevel endExclusive) {
+    return withApiFilter(api -> api.getLevel() < endExclusive.getLevel());
   }
 
   public TestParametersCollection build() {
@@ -158,15 +181,31 @@
   }
 
   public Stream<TestParameters> createParameters(TestRuntime runtime) {
-    if (enableAllApiLevels && runtime.isDex()) {
-      AndroidApiLevel vmLevel = runtime.asDex().getMinApiLevel();
-      if (vmLevel != lowestCompilerApiLevel) {
-        return Stream.of(
-            new TestParameters(runtime, vmLevel),
-            new TestParameters(runtime, lowestCompilerApiLevel));
+    if (!enableApiLevels || !runtime.isDex()) {
+      return Stream.of(new TestParameters(runtime));
+    }
+    List<AndroidApiLevel> sortedApiLevels =
+        Arrays.stream(AndroidApiLevel.values()).filter(apiLevelFilter).collect(Collectors.toList());
+    if (sortedApiLevels.isEmpty()) {
+      return Stream.of();
+    }
+    AndroidApiLevel vmLevel = runtime.asDex().getMinApiLevel();
+    AndroidApiLevel lowestApplicable = sortedApiLevels.get(sortedApiLevels.size() - 1);
+    if (vmLevel.getLevel() < lowestApplicable.getLevel()) {
+      return Stream.of();
+    }
+    if (sortedApiLevels.size() > 1) {
+      for (int i = 0; i < sortedApiLevels.size(); i++) {
+        AndroidApiLevel highestApplicable = sortedApiLevels.get(i);
+        if (highestApplicable.getLevel() <= vmLevel.getLevel()
+            && lowestApplicable != highestApplicable) {
+          return Stream.of(
+              new TestParameters(runtime, lowestApplicable),
+              new TestParameters(runtime, highestApplicable));
+        }
       }
     }
-    return Stream.of(new TestParameters(runtime));
+    return Stream.of(new TestParameters(runtime, lowestApplicable));
   }
 
   // Public method to check that the CF runtime coincides with the system runtime.
diff --git a/src/test/java/com/android/tools/r8/annotations/RetentionPolicyTest.java b/src/test/java/com/android/tools/r8/annotations/RetentionPolicyTest.java
new file mode 100644
index 0000000..be166ec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/RetentionPolicyTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, 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.annotations;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetentionPolicyTest extends TestBase {
+
+  private static final Collection<Class<?>> CLASSES =
+      ImmutableList.of(ClassRetained.class, SourceRetained.class, RuntimeRetained.class, A.class);
+
+  private static final String EXPECTED =
+      StringUtils.lines("@" + RuntimeRetained.class.getName() + "()");
+
+  @Parameters(name = "{0}, intermediate:{1}")
+  public static List<Object> data() {
+    return getTestParameters().withAllRuntimes().build().stream()
+        .flatMap(
+            parameters -> {
+              if (parameters.isCfRuntime()) {
+                return Stream.of((Object) new Object[] {parameters, false});
+              }
+              return Stream.of(new Object[] {parameters, true}, new Object[] {parameters, false});
+            })
+        .collect(Collectors.toList());
+  }
+
+  private final TestParameters parameters;
+  private final boolean intermediate;
+
+  public RetentionPolicyTest(TestParameters parameters, boolean intermediate) {
+    this.parameters = parameters;
+    this.intermediate = intermediate;
+  }
+
+  @Retention(RetentionPolicy.CLASS)
+  @interface ClassRetained {}
+
+  @Retention(RetentionPolicy.SOURCE)
+  @interface SourceRetained {}
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface RuntimeRetained {}
+
+  @ClassRetained
+  @SourceRetained
+  @RuntimeRetained
+  public static class A {
+
+    public static void main(String[] args) {
+      for (Annotation annotation : A.class.getAnnotations()) {
+        System.out.println(annotation);
+      }
+    }
+  }
+
+  @Test
+  public void test() throws Exception {
+    TestBuilder<?, ?> testBuilder =
+        parameters.isCfRuntime()
+            ? testForJvm()
+            : testForD8().setMinApi(parameters.getRuntime()).setIntermediate(intermediate);
+
+    CodeInspector inspector =
+        testBuilder
+            .addProgramClasses(CLASSES)
+            .run(parameters.getRuntime(), A.class)
+            .assertSuccessWithOutput(EXPECTED)
+            .inspector();
+
+    ClassSubject clazz = inspector.clazz(A.class);
+    assertThat(clazz, isPresent());
+    // Source retained annotations are always gone, even in the CF inputs.
+    assertFalse(clazz.annotation(SourceRetained.class.getName()).isPresent());
+    // Class retained annotations are present in CF and in intermediate builds.
+    assertEquals(
+        parameters.isCfRuntime() || intermediate,
+        clazz.annotation(ClassRetained.class.getName()).isPresent());
+    // Runtime retained annotations are present in all.
+    assertTrue(clazz.annotation(RuntimeRetained.class.getName()).isPresent());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
index f1f2842..eaa8718 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
 import com.android.tools.r8.AsmTestBase;
@@ -20,8 +21,12 @@
 
 public class CovariantReturnTypeAnnotationTransformerTest extends AsmTestBase {
   public static final String PACKAGE_NAME = "com/android/tools/r8/ir/desugar/annotations";
-  public static final String CRT_NAME = "dalvik/annotation/codegen/CovariantReturnType";
-  public static final String CRTS_SIMPLE_NAME = "CovariantReturnTypes";
+  public static final String CRT_BINARY_NAME = "dalvik/annotation/codegen/CovariantReturnType";
+  public static final String CRTS_INNER_NAME = "CovariantReturnTypes";
+  public static final String CRTS_BINARY_NAME = CRT_BINARY_NAME + "$" + CRTS_INNER_NAME;
+
+  public static final String CRT_TYPE_NAME = CRT_BINARY_NAME.replace('/', '.');
+  public static final String CRTS_TYPE_NAME = CRT_BINARY_NAME.replace('/', '.');
 
   @Test
   public void testVersion1WithClient1And2() throws Exception {
@@ -32,6 +37,9 @@
             ToolHelper.getClassAsBytes(B.class),
             ToolHelper.getClassAsBytes(C.class));
 
+    // Version 1 does not contain annotations.
+    checkPresenceOfCovariantAnnotations(input, false);
+
     // Version 1 of the library should always work.
     succeedsIndependentOfFlag(input, false);
   }
@@ -45,6 +53,9 @@
             ToolHelper.getClassAsBytes(B.class),
             ToolHelper.getClassAsBytes(C.class));
 
+    // Version 1 does not contain annotations.
+    checkPresenceOfCovariantAnnotations(input, false);
+
     // There will be no methods with the signature "L.../B;->method()L.../B;" and
     // "L.../C;->method()L.../C;".
     failsIndependentOfFlag(input);
@@ -59,6 +70,9 @@
             com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(),
             com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump());
 
+    // Version 2 contains annotations.
+    checkPresenceOfCovariantAnnotations(input, true);
+
     // Version 2 of the library should always work.
     succeedsIndependentOfFlag(input, true);
   }
@@ -72,6 +86,9 @@
             com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(),
             com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump());
 
+    // Version 2 contains annotations.
+    checkPresenceOfCovariantAnnotations(input, true);
+
     // If CovariantReturnType annotations are processed, then synthetic methods with the signatures
     // "L.../B;->method()L.../B;" and "L.../C;->method()L.../C;" will be added by D8.
     succeedsWithOption(input, true, true);
@@ -90,6 +107,9 @@
             com.android.tools.r8.ir.desugar.annotations.version3.BDump.dump(),
             com.android.tools.r8.ir.desugar.annotations.version3.CDump.dump());
 
+    // Version 3 does not contain annotations.
+    checkPresenceOfCovariantAnnotations(input, false);
+
     // Version 3 of the library should always work.
     succeedsIndependentOfFlag(input, false);
   }
@@ -103,6 +123,9 @@
             com.android.tools.r8.ir.desugar.annotations.version3.BDump.dump(),
             com.android.tools.r8.ir.desugar.annotations.version3.CDump.dump());
 
+    // Version 3 does not contain annotations.
+    checkPresenceOfCovariantAnnotations(input, false);
+
     // Version 3 of the library should always work with client 1.
     succeedsIndependentOfFlag(input, false);
   }
@@ -116,9 +139,15 @@
             com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(),
             com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump());
 
+    // Version 2 contains annotations.
+    checkPresenceOfCovariantAnnotations(input, true);
+
     AndroidApp output =
         compileWithD8(input, options -> options.processCovariantReturnTypeAnnotations = true);
 
+    // Compilation output does not contain annotations.
+    checkPresenceOfCovariantAnnotations(output, false);
+
     // Compilation will fail with a compilation error the second time if the implementation does
     // not remove the CovariantReturnType annotations properly during the first compilation.
     compileWithD8(output, options -> options.processCovariantReturnTypeAnnotations = true);
@@ -129,8 +158,8 @@
     AndroidApp output =
         compileWithD8(input, options -> options.processCovariantReturnTypeAnnotations = option);
     String stdout = runOnArt(output, Client.class.getCanonicalName());
-    Assert.assertEquals(getExpectedOutput(), stdout);
-
+    assertEquals(getExpectedOutput(), stdout);
+    checkPresenceOfCovariantAnnotations(output, false);
     if (option && checkPresenceOfSyntheticMethods) {
       checkPresenceOfSyntheticMethods(output);
     }
@@ -139,6 +168,7 @@
   private void failsWithOption(AndroidApp input, boolean option) throws Exception {
     AndroidApp output =
         compileWithD8(input, options -> options.processCovariantReturnTypeAnnotations = option);
+    checkPresenceOfCovariantAnnotations(output, false);
     ToolHelper.ProcessResult result = runOnArtRaw(output, Client.class.getCanonicalName());
     assertThat(result.stderr, containsString("java.lang.NoSuchMethodError"));
   }
@@ -154,6 +184,18 @@
     failsWithOption(input, false);
   }
 
+  private void checkPresenceOfCovariantAnnotations(AndroidApp app, boolean expected)
+      throws Exception {
+    CodeInspector inspector = new CodeInspector(app);
+    assertEquals(
+        expected,
+        inspector.allClasses().stream()
+            .anyMatch(
+                clazz ->
+                    clazz.allMethods().stream()
+                        .anyMatch(method -> method.annotation(CRTS_TYPE_NAME).isPresent())));
+  }
+
   private void checkPresenceOfSyntheticMethods(AndroidApp output) throws Exception {
     CodeInspector inspector = new CodeInspector(output);
 
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/BDump.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/BDump.java
index 3dadc2b..1b6d3ef 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/BDump.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/BDump.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.annotations.version2;
 
-import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.CRT_NAME;
+import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.CRT_BINARY_NAME;
 import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.PACKAGE_NAME;
 
 import org.objectweb.asm.AnnotationVisitor;
@@ -40,7 +40,7 @@
     {
       mv = cw.visitMethod(ACC_PUBLIC, "method", "()L" + PACKAGE_NAME + "/A;", null, null);
       {
-        av0 = mv.visitAnnotation("L" + CRT_NAME + ";", false);
+        av0 = mv.visitAnnotation("L" + CRT_BINARY_NAME + ";", false);
         av0.visit("returnType", Type.getType("L" + PACKAGE_NAME + "/B;"));
         av0.visit("presentAfter", new Integer(25));
         av0.visitEnd();
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/CDump.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/CDump.java
index 30cf603..2838141 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/CDump.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/CDump.java
@@ -4,8 +4,9 @@
 
 package com.android.tools.r8.ir.desugar.annotations.version2;
 
-import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.CRTS_SIMPLE_NAME;
-import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.CRT_NAME;
+import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.CRTS_BINARY_NAME;
+import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.CRTS_INNER_NAME;
+import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.CRT_BINARY_NAME;
 import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.PACKAGE_NAME;
 
 import org.objectweb.asm.AnnotationVisitor;
@@ -31,9 +32,9 @@
     cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, PACKAGE_NAME + "/C", null, PACKAGE_NAME + "/B", null);
 
     cw.visitInnerClass(
-        CRT_NAME + "$" + CRTS_SIMPLE_NAME,
-        CRT_NAME,
-        CRTS_SIMPLE_NAME,
+        CRTS_BINARY_NAME,
+        CRT_BINARY_NAME,
+        CRTS_INNER_NAME,
         ACC_PUBLIC + ACC_STATIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE);
 
     {
@@ -48,17 +49,17 @@
     {
       mv = cw.visitMethod(ACC_PUBLIC, "method", "()L" + PACKAGE_NAME + "/A;", null, null);
       {
-        av0 = mv.visitAnnotation("L" + CRT_NAME + "$" + CRTS_SIMPLE_NAME + ";", false);
+        av0 = mv.visitAnnotation("L" + CRTS_BINARY_NAME + ";", false);
         {
           AnnotationVisitor av1 = av0.visitArray("value");
           {
-            AnnotationVisitor av2 = av1.visitAnnotation(null, "L" + CRT_NAME + ";");
+            AnnotationVisitor av2 = av1.visitAnnotation(null, "L" + CRT_BINARY_NAME + ";");
             av2.visit("returnType", Type.getType("L" + PACKAGE_NAME + "/B;"));
             av2.visit("presentAfter", new Integer(25));
             av2.visitEnd();
           }
           {
-            AnnotationVisitor av2 = av1.visitAnnotation(null, "L" + CRT_NAME + ";");
+            AnnotationVisitor av2 = av1.visitAnnotation(null, "L" + CRT_BINARY_NAME + ";");
             av2.visit("returnType", Type.getType("L" + PACKAGE_NAME + "/C;"));
             av2.visit("presentAfter", new Integer(28));
             av2.visitEnd();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
new file mode 100644
index 0000000..94e2a9c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2019, 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.maindexlist;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MainDexWithSynthesizedClassesTest extends TestBase {
+
+  static final AndroidApiLevel nativeMultiDexLevel = AndroidApiLevel.L;
+
+  static final String EXPECTED = StringUtils.lines("AB");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withAllRuntimes()
+        .withApiLevelsEndingAtExcluding(nativeMultiDexLevel)
+        .build();
+  }
+
+  public MainDexWithSynthesizedClassesTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testFinal() throws Exception {
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addTestClasspath()
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(EXPECTED);
+    } else {
+      D8TestCompileResult compileResult =
+          testForD8()
+              .addInnerClasses(MainDexWithSynthesizedClassesTest.class)
+              .addMainDexListClasses(TestClass.class, A.class)
+              .setMinApiThreshold(parameters.getApiLevel())
+              .compile();
+      checkCompilationResult(compileResult);
+    }
+  }
+
+  @Test
+  public void testIntermediate() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    D8TestCompileResult intermediateResult =
+        testForD8()
+            .addInnerClasses(MainDexWithSynthesizedClassesTest.class)
+            .setMinApiThreshold(parameters.getApiLevel())
+            .setIntermediate(true)
+            .compile();
+    D8TestCompileResult compileResult =
+        testForD8()
+            .addProgramFiles(intermediateResult.writeToZip())
+            .addMainDexListClasses(TestClass.class, A.class)
+            .setMinApiThreshold(parameters.getApiLevel())
+            .compile();
+    checkCompilationResult(compileResult);
+  }
+
+  private void checkCompilationResult(D8TestCompileResult compileResult) throws Exception {
+    if (parameters.getRuntime().asDex().getMinApiLevel().getLevel()
+        < nativeMultiDexLevel.getLevel()) {
+      compileResult
+          .runDex2Oat(parameters.getRuntime().asDex().getVm())
+          .assertNoVerificationErrors();
+    } else {
+      compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput(EXPECTED);
+    }
+    Path out = temp.newFolder().toPath();
+    compileResult.apply(b -> b.app.writeToDirectory(out, OutputMode.DexIndexed));
+    Path classes = out.resolve("classes.dex");
+    Path classes2 = out.resolve("classes2.dex");
+    assertTrue(Files.exists(classes));
+    assertTrue(Files.exists(classes2));
+    checkContainsLambdaClasses(new CodeInspector(classes), A.class);
+    checkContainsLambdaClasses(new CodeInspector(classes2), B.class);
+  }
+
+  private void checkContainsLambdaClasses(CodeInspector inspector, Class<?> lambdaHolder) {
+    assertTrue(
+        inspector.allClasses().stream()
+            .anyMatch(
+                clazz ->
+                    clazz.getOriginalName().contains(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX)
+                        && clazz
+                            .getOriginalName()
+                            .contains("$" + lambdaHolder.getSimpleName() + "$")));
+  }
+
+  static class TestClass {
+    public static void main(String[] args) {
+      System.out.println(new A().foo().get());
+    }
+  }
+
+  interface Getter {
+    String get();
+  }
+
+  static class A {
+    Getter foo() {
+      return () -> "A" + new B().foo().get();
+    }
+  }
+
+  static class B {
+    Getter foo() {
+      return () -> "B";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index f585226..2d30f24 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -95,11 +95,7 @@
       obfuscatedToOriginalMapping = null;
     }
     Timing timing = new Timing("CodeInspector");
-    InternalOptions options = new InternalOptions();
-    options.enableCfFrontend = true;
-    if (optionsConsumer != null) {
-      optionsConsumer.accept(options);
-    }
+    InternalOptions options = runOptionsConsumer(optionsConsumer);
     dexItemFactory = options.itemFactory;
     AndroidApp input = AndroidApp.builder().addProgramFiles(files).build();
     application = new ApplicationReader(input, options, timing).read();