Merge commit 'd052837ac460c9f5bc2cb7ff6cf7ee8319f6e5ac' into dev-release

Change-Id: Ib07bd74ccbb0b242d989763d35e3a05343173a33
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5586e26..4b76e69 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -730,19 +730,19 @@
       // already have been leveraged.
       OptimizationInfoRemover.run(appView, executorService);
 
+      // Perform repackaging.
+      if (appView.hasLiveness()) {
+        if (options.isRepackagingEnabled()) {
+          new Repackaging(appView.withLiveness()).run(executorService, timing);
+        }
+        assert Repackaging.verifyIdentityRepackaging(appView.withLiveness(), executorService);
+      }
+
       GenericSignatureContextBuilder genericContextBuilderBeforeFinalMerging = null;
       if (appView.hasCfByteCodePassThroughMethods()) {
         LirConverter.rewriteLirWithLens(appView, timing, executorService);
         appView.clearCodeRewritings(executorService, timing);
       } else {
-        // Perform repackaging.
-        if (appView.hasLiveness()) {
-          if (options.isRepackagingEnabled()) {
-            new Repackaging(appView.withLiveness()).run(executorService, timing);
-          }
-          assert Repackaging.verifyIdentityRepackaging(appView.withLiveness(), executorService);
-        }
-
         // Rewrite LIR with lens to allow building IR from LIR in class mergers.
         LirConverter.rewriteLirWithLens(appView, timing, executorService);
         appView.clearCodeRewritings(executorService, timing);
diff --git a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
index 47357cd..9b32d2d 100644
--- a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
@@ -93,10 +93,10 @@
   }
 
   public static boolean shouldRun(AppView<?> appView) {
-    if (!appView.options().processCovariantReturnTypeAnnotations) {
+    if (!appView.options().processCovariantReturnTypeAnnotations
+        || appView.options().isDesugaredLibraryCompilation()) {
       return false;
     }
-    assert !appView.options().isDesugaredLibraryCompilation();
     DexItemFactory factory = appView.dexItemFactory();
     DexString covariantReturnTypeDescriptor =
         factory.createString(CovariantReturnTypeReferences.COVARIANT_RETURN_TYPE_DESCRIPTOR);
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index cee4f28..10f0699 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
@@ -151,7 +152,12 @@
     DexString descriptor = annotationType.getDescriptor();
     if (descriptor.startsWith(factory.dalvikAnnotationPrefix)) {
       if (descriptor.startsWith(factory.dalvikAnnotationCodegenCovariantReturnTypePrefix)) {
-        return options.processCovariantReturnTypeAnnotations;
+        if (options.processCovariantReturnTypeAnnotations) {
+          return true;
+        }
+        throw options.reporter.fatalError(
+            new StringDiagnostic(
+                "Unexpected @CovariantReturnType annotation in non-platform build"));
       }
       return descriptor.startsWith(factory.dalvikAnnotationOptimizationPrefix);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
index 0cc9356..84bd5a1 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -53,7 +54,6 @@
   }
 
   public static class TypeParameterContext {
-
     private static final TypeParameterContext EMPTY =
         new TypeParameterContext(Collections.emptyMap(), Collections.emptySet());
 
@@ -207,17 +207,38 @@
   public TypeParameterContext computeTypeParameterContext(
       AppView<?> appView, DexReference reference, Predicate<DexType> wasPruned) {
     assert !wasPruned.test(reference.getContextType()) : "Building context for pruned type";
-    return computeTypeParameterContext(appView, reference, wasPruned, false);
+    return computeTypeParameterContext(appView, reference, wasPruned, false, null);
   }
 
   private TypeParameterContext computeTypeParameterContext(
       AppView<?> appView,
       DexReference reference,
       Predicate<DexType> wasPruned,
-      boolean seenPruned) {
+      boolean seenPruned,
+      Object seen) {
     if (reference == null) {
       return empty();
     }
+
+    // Optimize seen set to be null when empty and a DexReference when it only has one element.
+    if (seen == null) {
+      seen = reference;
+    } else if (seen instanceof DexReference) {
+      if (seen == reference) {
+        return empty();
+      }
+      DexReference tmp = (DexReference) seen;
+      seen = Sets.newIdentityHashSet();
+      ((Set<DexReference>) seen).add(tmp);
+    }
+    assert seen != null;
+    assert seen instanceof DexReference || seen instanceof Set;
+    if (seen instanceof Set) {
+      if (!((Set<DexReference>) seen).add(reference)) {
+        return empty();
+      }
+    }
+
     DexType contextType = reference.getContextType();
     // TODO(b/187035453): We should visit generic signatures in the enqueuer.
     DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(contextType);
@@ -242,7 +263,8 @@
                 enclosingReference,
                 wasPruned,
                 prunedHere
-                    || hasPrunedRelationship(appView, enclosingReference, contextType, wasPruned))
+                    || hasPrunedRelationship(appView, enclosingReference, contextType, wasPruned),
+                seen)
             // Add formals for the context
             .combine(formalsInfo, prunedHere);
     if (!reference.isDexMethod()) {
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 be7b139..cf0c2b9 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull;
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO;
 import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
@@ -57,7 +56,6 @@
 import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
@@ -164,7 +162,6 @@
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.Visibility;
 import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
 import com.android.tools.r8.utils.collections.ProgramFieldSet;
 import com.android.tools.r8.utils.collections.ProgramMethodMap;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -5484,7 +5481,7 @@
   }
 
   private void handleServiceLoaderInvocation(ProgramMethod method, InvokeMethod invoke) {
-    if (invoke.inValues().size() == 0) {
+    if (invoke.inValues().isEmpty()) {
       // Should never happen.
       return;
     }
@@ -5510,7 +5507,6 @@
       DexType serviceType, ProgramMethod context, KeepReason reason) {
     List<DexType> serviceImplementationTypes =
         appView.appServices().serviceImplementationsFor(serviceType);
-    DexMethodSignatureSet serviceMethods = getServiceMethods(serviceType, context);
     for (DexType serviceImplementationType : serviceImplementationTypes) {
       if (!serviceImplementationType.isClassType()) {
         // Should never happen.
@@ -5530,35 +5526,9 @@
         applyMinimumKeepInfoWhenLiveOrTargeted(
             defaultInitializer, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
       }
-
-      for (DexMethodSignature serviceMethod : serviceMethods) {
-        ProgramMethod serviceImplementationMethod =
-            asProgramMethodOrNull(
-                serviceImplementationClass.getMethodCollection().getMethod(serviceMethod),
-                serviceImplementationClass);
-        if (serviceImplementationMethod != null) {
-          applyMinimumKeepInfoWhenLiveOrTargeted(
-              serviceImplementationMethod, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
-        }
-      }
     }
   }
 
-  private DexMethodSignatureSet getServiceMethods(DexType serviceType, ProgramMethod context) {
-    DexMethodSignatureSet serviceMethods = DexMethodSignatureSet.create();
-    WorkList<DexType> serviceTypes = WorkList.newIdentityWorkList(serviceType);
-    while (serviceTypes.hasNext()) {
-      DexType current = serviceTypes.next();
-      DexClass clazz = getClassOrNullFromReflectiveAccess(current, context);
-      if (clazz == null) {
-        continue;
-      }
-      clazz.forEachClassMethodMatching(DexEncodedMethod::belongsToVirtualPool, serviceMethods::add);
-      serviceTypes.addIfNotSeen(clazz.getInterfaces());
-    }
-    return serviceMethods;
-  }
-
   private static class SetWithReportedReason<T> {
 
     private final Set<T> items = Sets.newIdentityHashSet();
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 29bab19..39fd650 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -786,7 +786,7 @@
   public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Auto;
   // Flag to turn on/off processing of @dalvik.annotation.codegen.CovariantReturnType and
   // @dalvik.annotation.codegen.CovariantReturnType$CovariantReturnTypes.
-  public boolean processCovariantReturnTypeAnnotations = false;
+  public boolean processCovariantReturnTypeAnnotations = true;
 
   public boolean loadAllClassDefinitions = false;
 
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingEnclosingConstructorTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingEnclosingConstructorTest.java
new file mode 100644
index 0000000..79c68c2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingEnclosingConstructorTest.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2024, 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.graph.genericsignature;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Iterator;
+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 GenericSignatureSelfReferencingEnclosingConstructorTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private static final String CONSTRUCTOR =
+      TestClass.class.getTypeName() + "$1A(" + TestClass.class.getTypeName() + ")";
+  private static final String EXPECTED_OUTPUT = StringUtils.lines(CONSTRUCTOR);
+  private static final String EXPECTED_OUTPUT_R8_DEX = StringUtils.lines("public " + CONSTRUCTOR);
+
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingEnclosingMethod())
+        // .addInnerClasses(getClass())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingEnclosingMethod())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingEnclosingMethod())
+        .addKeepMainRule(TestClass.class)
+        .addKeepAttributeInnerClassesAndEnclosingMethod()
+        .addKeepRules("-keep class **$1A { <methods>; }")
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(
+            parameters.isDexRuntime() ? EXPECTED_OUTPUT_R8_DEX : EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8NoKeep() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingEnclosingMethod())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("null");
+  }
+
+  // Change the EnclosingMethod attribute for the local class A to be a constructor of A itself.
+  private byte[] getProgramClassFileDataWithSelfReferencingEnclosingMethod() throws IOException {
+    Iterator<Path> innerClassesIterator =
+        ToolHelper.getClassFilesForInnerClasses(TestClass.class).iterator();
+    Path innerClass = innerClassesIterator.next();
+    assertFalse(innerClassesIterator.hasNext());
+    return transformer(
+            innerClass, Reference.classFromBinaryName(binaryName(TestClass.class) + "$1A"))
+        .rewriteEnclosingMethod(
+            binaryName(TestClass.class) + "$1A", "<init>", "(" + descriptor(TestClass.class) + ")V")
+        .transform();
+  }
+
+  static class TestClass {
+    TestClass() {
+      class A {
+        public void m() {
+          System.out.println(A.class.getEnclosingConstructor());
+        }
+      }
+      new A().m();
+    }
+
+    public static void main(String[] args) {
+      new TestClass();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingEnclosingMethodTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingEnclosingMethodTest.java
new file mode 100644
index 0000000..92685d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingEnclosingMethodTest.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2024, 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.graph.genericsignature;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Iterator;
+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 GenericSignatureSelfReferencingEnclosingMethodTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private static final String METHOD_M = "public void " + TestClass.class.getTypeName() + "$1A.m()";
+  private static final String EXPECTED_OUTPUT = StringUtils.lines(METHOD_M, METHOD_M);
+
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingEnclosingMethod())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingEnclosingMethod())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingEnclosingMethod())
+        .addKeepMainRule(TestClass.class)
+        .addKeepAttributeInnerClassesAndEnclosingMethod()
+        .addKeepRules("-keep class **$1A { <methods>; }")
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8NoKeep() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingEnclosingMethod())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("No method m", "null");
+  }
+
+  // Change the EnclosingMethod attribute for the local class A to be method m of A itself.
+  private byte[] getProgramClassFileDataWithSelfReferencingEnclosingMethod() throws IOException {
+    Iterator<Path> innerClassesIterator =
+        ToolHelper.getClassFilesForInnerClasses(TestClass.class).iterator();
+    Path innerClass = innerClassesIterator.next();
+    assertFalse(innerClassesIterator.hasNext());
+    return transformer(
+            innerClass, Reference.classFromBinaryName(binaryName(TestClass.class) + "$1A"))
+        .rewriteEnclosingMethod(binaryName(TestClass.class) + "$1A", "m", "()V")
+        .transform();
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      class A {
+        public void m() {
+          try {
+            try {
+              System.out.println(getClass().getMethod("m"));
+            } catch (NoSuchMethodException e) {
+              System.out.println("No method m");
+            }
+            System.out.println(A.class.getEnclosingMethod());
+          } catch (Exception e) {
+            System.out.println("Unexpected " + e);
+          }
+        }
+      }
+      new A().m();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingInnerClassesTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingInnerClassesTest.java
new file mode 100644
index 0000000..446978f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureSelfReferencingInnerClassesTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2024, 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.graph.genericsignature;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.genericsignature.GenericSignatureSelfReferencingInnerClassesTest.A.B;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Iterator;
+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 GenericSignatureSelfReferencingInnerClassesTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private static final String CLASS_B = "class " + A.B.class.getTypeName();
+  private static final String EXPECTED_OUTPUT = StringUtils.lines(CLASS_B, CLASS_B);
+
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingInnerClass())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertFailureWithErrorThatThrows(ClassFormatError.class);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingInnerClass())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingInnerClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepAttributeInnerClassesAndEnclosingMethod()
+        .addKeepRules("-keep class **B { <methods>; }")
+        .setMinApi(parameters)
+        .allowDiagnosticInfoMessages()
+        .compile()
+        .assertAllInfoMessagesMatch(containsString("Malformed inner-class attribute"))
+        .run(parameters.getRuntime(), TestClass.class)
+        .applyIf(
+            parameters.isCfRuntime(),
+            r -> r.assertFailureWithErrorThatThrows(ClassFormatError.class),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+  }
+
+  @Test
+  public void testR8NoKeep() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(getProgramClassFileDataWithSelfReferencingInnerClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .allowDiagnosticInfoMessages()
+        .compile()
+        .assertAllInfoMessagesMatch(containsString("Malformed inner-class attribute"))
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("class " + getClass().getPackage().getName() + ".a", "null");
+  }
+
+  // Change the InnerClasses attribute for the inner class B to be B itself.
+  private byte[] getProgramClassFileDataWithSelfReferencingInnerClass() throws IOException {
+    Iterator<Path> innerClassesIterator =
+        ToolHelper.getClassFilesForInnerClasses(A.class).iterator();
+    Path innerClass = innerClassesIterator.next();
+    assertFalse(innerClassesIterator.hasNext());
+    return transformer(innerClass, Reference.classFromBinaryName(binaryName(A.B.class)))
+        .rewriteInnerClass(binaryName(A.B.class), binaryName(A.B.class), "B")
+        .transform();
+  }
+
+  static class A {
+    static class B {}
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println(B.class);
+      System.out.println(B.class.getEnclosingClass());
+    }
+  }
+}
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 7853976..56a2289 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
@@ -4,14 +4,21 @@
 
 package com.android.tools.r8.ir.desugar.annotations;
 
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static com.android.tools.r8.utils.codeinspector.AssertUtils.assertFailsCompilation;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.AllOf.allOf;
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.AsmTestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -19,7 +26,6 @@
 import java.nio.file.Path;
 import java.util.Collections;
 import java.util.List;
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -34,7 +40,6 @@
   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('/', '.');
 
   @Parameter(0)
@@ -100,7 +105,8 @@
     checkPresenceOfCovariantAnnotations(input, true);
 
     // Version 2 of the library should always work.
-    succeedsIndependentOfFlag(input, true);
+    succeedsWithOption(input, true, true);
+    failsCompilationWhenDisabled(input);
   }
 
   @Test
@@ -124,7 +130,7 @@
 
     // If CovariantReturnType annotations are ignored, then there will be no methods with the
     // signatures "L.../B;->method()L.../B;" and "L.../C;->method()L.../C;".
-    failsWithOption(input, false);
+    failsCompilationWhenDisabled(input);
   }
 
   @Test
@@ -218,7 +224,26 @@
         .assertSuccessWithOutput(getExpectedOutput());
   }
 
-  private void failsWithOption(List<byte[]> input, boolean option) throws Exception {
+  private void failsCompilationWhenDisabled(List<byte[]> input) throws Exception {
+    assertFailsCompilation(
+        () ->
+            testForD8(parameters.getBackend())
+                .addProgramClassFileData(input)
+                .addOptionsModification(
+                    options -> options.processCovariantReturnTypeAnnotations = false)
+                .setMinApi(parameters)
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics.assertErrorThatMatches(
+                            allOf(
+                                diagnosticType(StringDiagnostic.class),
+                                diagnosticMessage(
+                                    equalTo(
+                                        "Unexpected @CovariantReturnType annotation in non-platform"
+                                            + " build"))))));
+  }
+
+  private void failsRuntimeWithOption(List<byte[]> input, boolean option) throws Exception {
     testForD8(parameters.getBackend())
         .addProgramClassFileData(input)
         .addOptionsModification(options -> options.processCovariantReturnTypeAnnotations = option)
@@ -236,8 +261,8 @@
   }
 
   private void failsIndependentOfFlag(List<byte[]> input) throws Exception {
-    failsWithOption(input, true);
-    failsWithOption(input, false);
+    failsRuntimeWithOption(input, true);
+    failsRuntimeWithOption(input, false);
   }
 
   private void checkPresenceOfCovariantAnnotations(List<byte[]> input, boolean expected)
@@ -271,34 +296,34 @@
     MethodSubject methodA =
         clazzA.method(A.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodA, isPresent());
-    Assert.assertTrue(!methodA.getMethod().isSyntheticMethod());
+    assertTrue(!methodA.getMethod().isSyntheticMethod());
 
     MethodSubject methodB =
         clazzB.method(A.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodB, isPresent());
-    Assert.assertTrue(!methodB.getMethod().isSyntheticMethod());
+    assertTrue(!methodB.getMethod().isSyntheticMethod());
 
     MethodSubject methodC =
         clazzC.method(A.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodC, isPresent());
-    Assert.assertTrue(!methodC.getMethod().isSyntheticMethod());
+    assertTrue(!methodC.getMethod().isSyntheticMethod());
 
     // Check that a synthetic method has been added to class B.
     MethodSubject methodB2 =
         clazzB.method(B.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodB2, isPresent());
-    Assert.assertTrue(methodB2.getMethod().isSyntheticMethod());
+    assertTrue(methodB2.getMethod().isSyntheticMethod());
 
     // Check that two synthetic methods have been added to class C.
     MethodSubject methodC2 =
         clazzC.method(B.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodC2, isPresent());
-    Assert.assertTrue(methodC2.getMethod().isSyntheticMethod());
+    assertTrue(methodC2.getMethod().isSyntheticMethod());
 
     MethodSubject methodC3 =
         clazzC.method(C.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodC3, isPresent());
-    Assert.assertTrue(methodC3.getMethod().isSyntheticMethod());
+    assertTrue(methodC3.getMethod().isSyntheticMethod());
 
     // Get classes D, E, and F.
     ClassSubject clazzD = inspector.clazz(D.class.getCanonicalName());
@@ -314,34 +339,34 @@
     MethodSubject methodD =
         clazzD.method(D.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodD, isPresent());
-    Assert.assertTrue(!methodD.getMethod().isSyntheticMethod());
+    assertTrue(!methodD.getMethod().isSyntheticMethod());
 
     MethodSubject methodE =
         clazzE.method(D.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodE, isPresent());
-    Assert.assertTrue(!methodE.getMethod().isSyntheticMethod());
+    assertTrue(!methodE.getMethod().isSyntheticMethod());
 
     MethodSubject methodF =
         clazzF.method(D.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodF, isPresent());
-    Assert.assertTrue(!methodF.getMethod().isSyntheticMethod());
+    assertTrue(!methodF.getMethod().isSyntheticMethod());
 
     // Check that a synthetic method has been added to class E.
     MethodSubject methodE2 =
         clazzE.method(E.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodE2, isPresent());
-    Assert.assertTrue(methodE2.getMethod().isSyntheticMethod());
+    assertTrue(methodE2.getMethod().isSyntheticMethod());
 
     // Check that two synthetic methods have been added to class F.
     MethodSubject methodF2 =
         clazzF.method(E.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodF2, isPresent());
-    Assert.assertTrue(methodF2.getMethod().isSyntheticMethod());
+    assertTrue(methodF2.getMethod().isSyntheticMethod());
 
     MethodSubject methodF3 =
         clazzF.method(F.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodF3, isPresent());
-    Assert.assertTrue(methodF3.getMethod().isSyntheticMethod());
+    assertTrue(methodF3.getMethod().isSyntheticMethod());
   }
 
   private String getExpectedOutput() {
diff --git a/src/test/testbase/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/testbase/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 69bd9b2..8b3e63c 100644
--- a/src/test/testbase/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/testbase/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -736,6 +736,17 @@
         });
   }
 
+  public ClassFileTransformer rewriteInnerClass(
+      String newName, String newOuterName, String newInnerName) {
+    return addClassTransformer(
+        new ClassTransformer() {
+          @Override
+          public void visitInnerClass(String name, String outerName, String innerName, int access) {
+            super.visitInnerClass(newName, newOuterName, newInnerName, access);
+          }
+        });
+  }
+
   public ClassFileTransformer rewriteEnlosingAndNestAttributes(Function<String, String> rewrite) {
     return addClassTransformer(
         new ClassTransformer() {
diff --git a/tools/archive_smali.py b/tools/archive_smali.py
index 0c877e5..e19da70 100755
--- a/tools/archive_smali.py
+++ b/tools/archive_smali.py
@@ -63,7 +63,7 @@
     if utils.is_bot() and not utils.IsWindows():
         set_rlimit_to_max()
 
-    utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
+    utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE)
     with utils.TempDir() as temp:
         # Resolve dry run location to support relative directories.
         dry_run_output = None
@@ -101,11 +101,11 @@
 
             print('Building version: %s' % version)
 
-            # Build release to local Maven repository compiling with JDK-8.
+            # Build release to local Maven repository compiling with JDK-11.
             m2 = os.path.join(temp, 'm2')
             os.mkdir(m2)
             env = os.environ.copy()
-            env["JAVA_HOME"] = jdk.GetJdk8Home()
+            env["JAVA_HOME"] = jdk.GetJdk11Home()
             subprocess.check_call([
                 './gradlew',
                 '-Dmaven.repo.local=%s' % m2, 'release', 'test',