Support @CovariantReturnType on abstract methods

Fixes: b/307689296
Change-Id: I3673717c8148fbdb47309964f40b9565c443b1b0
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 76a4b02..aa69f8f 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
@@ -173,6 +173,7 @@
     MethodAccessFlags newAccessFlags = methodDefinition.accessFlags.copy();
     newAccessFlags.setBridge();
     newAccessFlags.setSynthetic();
+    newAccessFlags.unsetAbstract(); // Synthetic bridge has code, so never abstract.
     DexMethod newMethod =
         factory.createMethod(methodHolder.getType(), newProto, methodReference.getName());
     ForwardMethodBuilder forwardMethodBuilder =
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/Client.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/Client.java
index 0c6bd44..2fb0363 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/Client.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/Client.java
@@ -9,9 +9,15 @@
     A a = new A().method();
     A b = new B().method();
     A c = new C().method();
+    D d = ((D) new F()).method();
+    D e = ((E) new F()).method();
+    D f = new F().method();
 
     System.out.println("a=" + a.getClass().getSimpleName());
     System.out.println("b=" + b.getClass().getSimpleName());
     System.out.println("c=" + c.getClass().getSimpleName());
+    System.out.println("d=" + d.getClass().getSimpleName());
+    System.out.println("e=" + e.getClass().getSimpleName());
+    System.out.println("f=" + f.getClass().getSimpleName());
   }
 }
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 4d2f87d..7853976 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
@@ -52,7 +52,10 @@
             ToolHelper.getClassAsBytes(Client.class),
             ToolHelper.getClassAsBytes(A.class),
             ToolHelper.getClassAsBytes(B.class),
-            ToolHelper.getClassAsBytes(C.class));
+            ToolHelper.getClassAsBytes(C.class),
+            ToolHelper.getClassAsBytes(D.class),
+            ToolHelper.getClassAsBytes(E.class),
+            ToolHelper.getClassAsBytes(F.class));
 
     // Version 1 does not contain annotations.
     checkPresenceOfCovariantAnnotations(input, false);
@@ -68,7 +71,10 @@
             com.android.tools.r8.ir.desugar.annotations.version3.ClientDump.dump(),
             ToolHelper.getClassAsBytes(A.class),
             ToolHelper.getClassAsBytes(B.class),
-            ToolHelper.getClassAsBytes(C.class));
+            ToolHelper.getClassAsBytes(C.class),
+            ToolHelper.getClassAsBytes(D.class),
+            ToolHelper.getClassAsBytes(E.class),
+            ToolHelper.getClassAsBytes(F.class));
 
     // Version 1 does not contain annotations.
     checkPresenceOfCovariantAnnotations(input, false);
@@ -85,7 +91,10 @@
             ToolHelper.getClassAsBytes(Client.class),
             ToolHelper.getClassAsBytes(A.class),
             com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(),
-            com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump());
+            com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump(),
+            ToolHelper.getClassAsBytes(D.class),
+            com.android.tools.r8.ir.desugar.annotations.version2.EDump.dump(),
+            com.android.tools.r8.ir.desugar.annotations.version2.FDump.dump());
 
     // Version 2 contains annotations.
     checkPresenceOfCovariantAnnotations(input, true);
@@ -101,7 +110,10 @@
             com.android.tools.r8.ir.desugar.annotations.version3.ClientDump.dump(),
             ToolHelper.getClassAsBytes(A.class),
             com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(),
-            com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump());
+            com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump(),
+            ToolHelper.getClassAsBytes(D.class),
+            com.android.tools.r8.ir.desugar.annotations.version2.EDump.dump(),
+            com.android.tools.r8.ir.desugar.annotations.version2.FDump.dump());
 
     // Version 2 contains annotations.
     checkPresenceOfCovariantAnnotations(input, true);
@@ -122,7 +134,10 @@
             com.android.tools.r8.ir.desugar.annotations.version3.ClientDump.dump(),
             ToolHelper.getClassAsBytes(A.class),
             com.android.tools.r8.ir.desugar.annotations.version3.BDump.dump(),
-            com.android.tools.r8.ir.desugar.annotations.version3.CDump.dump());
+            com.android.tools.r8.ir.desugar.annotations.version3.CDump.dump(),
+            ToolHelper.getClassAsBytes(D.class),
+            com.android.tools.r8.ir.desugar.annotations.version3.EDump.dump(),
+            com.android.tools.r8.ir.desugar.annotations.version3.FDump.dump());
 
     // Version 3 does not contain annotations.
     checkPresenceOfCovariantAnnotations(input, false);
@@ -138,7 +153,10 @@
             ToolHelper.getClassAsBytes(Client.class),
             ToolHelper.getClassAsBytes(A.class),
             com.android.tools.r8.ir.desugar.annotations.version3.BDump.dump(),
-            com.android.tools.r8.ir.desugar.annotations.version3.CDump.dump());
+            com.android.tools.r8.ir.desugar.annotations.version3.CDump.dump(),
+            ToolHelper.getClassAsBytes(D.class),
+            com.android.tools.r8.ir.desugar.annotations.version3.EDump.dump(),
+            com.android.tools.r8.ir.desugar.annotations.version3.FDump.dump());
 
     // Version 3 does not contain annotations.
     checkPresenceOfCovariantAnnotations(input, false);
@@ -154,7 +172,10 @@
             ToolHelper.getClassAsBytes(Client.class),
             ToolHelper.getClassAsBytes(A.class),
             com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(),
-            com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump());
+            com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump(),
+            ToolHelper.getClassAsBytes(D.class),
+            com.android.tools.r8.ir.desugar.annotations.version2.EDump.dump(),
+            com.android.tools.r8.ir.desugar.annotations.version2.FDump.dump());
 
     // Version 2 contains annotations.
     checkPresenceOfCovariantAnnotations(input, true);
@@ -248,7 +269,7 @@
 
     // Check that the original methods are there, and that they are not synthetic.
     MethodSubject methodA =
-        clazzB.method(A.class.getCanonicalName(), "method", Collections.emptyList());
+        clazzA.method(A.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodA, isPresent());
     Assert.assertTrue(!methodA.getMethod().isSyntheticMethod());
 
@@ -278,9 +299,52 @@
         clazzC.method(C.class.getCanonicalName(), "method", Collections.emptyList());
     assertThat(methodC3, isPresent());
     Assert.assertTrue(methodC3.getMethod().isSyntheticMethod());
+
+    // Get classes D, E, and F.
+    ClassSubject clazzD = inspector.clazz(D.class.getCanonicalName());
+    assertThat(clazzD, isPresent());
+
+    ClassSubject clazzE = inspector.clazz(E.class.getCanonicalName());
+    assertThat(clazzE, isPresent());
+
+    ClassSubject clazzF = inspector.clazz(F.class.getCanonicalName());
+    assertThat(clazzF, isPresent());
+
+    // Check that the original methods are there, and that they are not synthetic.
+    MethodSubject methodD =
+        clazzD.method(D.class.getCanonicalName(), "method", Collections.emptyList());
+    assertThat(methodD, isPresent());
+    Assert.assertTrue(!methodD.getMethod().isSyntheticMethod());
+
+    MethodSubject methodE =
+        clazzE.method(D.class.getCanonicalName(), "method", Collections.emptyList());
+    assertThat(methodE, isPresent());
+    Assert.assertTrue(!methodE.getMethod().isSyntheticMethod());
+
+    MethodSubject methodF =
+        clazzF.method(D.class.getCanonicalName(), "method", Collections.emptyList());
+    assertThat(methodF, isPresent());
+    Assert.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());
+
+    // 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());
+
+    MethodSubject methodF3 =
+        clazzF.method(F.class.getCanonicalName(), "method", Collections.emptyList());
+    assertThat(methodF3, isPresent());
+    Assert.assertTrue(methodF3.getMethod().isSyntheticMethod());
   }
 
   private String getExpectedOutput() {
-    return "a=A\nb=B\nc=C\n";
+    return "a=A\nb=B\nc=C\nd=F\ne=F\nf=F\n";
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/D.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/D.java
new file mode 100644
index 0000000..038c59c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/D.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations;
+
+public abstract class D {
+  public abstract D method();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/E.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/E.java
new file mode 100644
index 0000000..fc62630
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/E.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations;
+
+public abstract class E extends D {
+  @Override
+  public abstract D method();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/F.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/F.java
new file mode 100644
index 0000000..b332595
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/F.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations;
+
+public class F extends E {
+  @Override
+  public D method() {
+    return new F();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/E.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/E.java
new file mode 100644
index 0000000..592c1b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/E.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations.version2;
+
+import com.android.tools.r8.ir.desugar.annotations.CovariantReturnType;
+import com.android.tools.r8.ir.desugar.annotations.D;
+
+public abstract class E extends D {
+  @CovariantReturnType(returnType = E.class, presentAfter = 25)
+  @Override
+  public abstract D method();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/EDump.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/EDump.java
new file mode 100644
index 0000000..1717468
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/EDump.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations.version2;
+
+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;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+// Generated by running tools/asmifier.py on build/classes/test/com/android/tools/r8/ir/desugar/
+// annotations/version2/E.class, removing the subpackage "version2" from all class names, and
+// manually changing the name of the CovariantReturnType annotation to
+// "Ldalvik/annotation/codegen/CovariantReturnType;".
+public class EDump implements Opcodes {
+  public static byte[] dump() throws Exception {
+
+    ClassWriter cw = new ClassWriter(0);
+    MethodVisitor mv;
+    AnnotationVisitor av0;
+
+    cw.visit(
+        V1_8,
+        ACC_PUBLIC | ACC_SUPER | ACC_ABSTRACT,
+        PACKAGE_NAME + "/E",
+        null,
+        PACKAGE_NAME + "/D",
+        null);
+
+    {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, PACKAGE_NAME + "/D", "<init>", "()V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    {
+      mv =
+          cw.visitMethod(
+              ACC_PUBLIC | ACC_ABSTRACT, "method", "()L" + PACKAGE_NAME + "/D;", null, null);
+      {
+        av0 = mv.visitAnnotation("L" + CRT_BINARY_NAME + ";", false);
+        av0.visit("returnType", Type.getType("L" + PACKAGE_NAME + "/E;"));
+        av0.visit("presentAfter", new Integer(25));
+        av0.visitEnd();
+      }
+      mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/F.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/F.java
new file mode 100644
index 0000000..a133826
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/F.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations.version2;
+
+import com.android.tools.r8.ir.desugar.annotations.CovariantReturnType;
+import com.android.tools.r8.ir.desugar.annotations.D;
+
+public class F extends E {
+  @CovariantReturnType(returnType = E.class, presentAfter = 25)
+  @CovariantReturnType(returnType = F.class, presentAfter = 28)
+  @Override
+  public D method() {
+    return new F();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/FDump.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/FDump.java
new file mode 100644
index 0000000..266bc46
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version2/FDump.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations.version2;
+
+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;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+// Generated by running tools/asmifier.py on build/classes/test/com/android/tools/r8/ir/desugar/
+// annotations/version2/F.class, removing the subpackage "version2" from all class names, and
+// manually changing the name of the CovariantReturnType and CovariantReturnTypes annotations to
+// "Ldalvik/annotation/codegen/CovariantReturnType;" and
+// "Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;", respectively.
+public class FDump implements Opcodes {
+  public static byte[] dump() throws Exception {
+
+    ClassWriter cw = new ClassWriter(0);
+    MethodVisitor mv;
+    AnnotationVisitor av0;
+
+    cw.visit(V1_8, ACC_PUBLIC | ACC_SUPER, PACKAGE_NAME + "/F", null, PACKAGE_NAME + "/E", null);
+
+    cw.visitInnerClass(
+        CRTS_BINARY_NAME,
+        CRT_BINARY_NAME,
+        CRTS_INNER_NAME,
+        ACC_PUBLIC | ACC_STATIC | ACC_ANNOTATION | ACC_ABSTRACT | ACC_INTERFACE);
+
+    {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, PACKAGE_NAME + "/E", "<init>", "()V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    {
+      mv =
+          cw.visitMethod(
+              ACC_PUBLIC,
+              "method",
+              "()Lcom/android/tools/r8/ir/desugar/annotations/D;",
+              null,
+              null);
+      {
+        av0 = mv.visitAnnotation("L" + CRTS_BINARY_NAME + ";", false);
+        {
+          AnnotationVisitor av1 = av0.visitArray("value");
+          {
+            AnnotationVisitor av2 = av1.visitAnnotation(null, "L" + CRT_BINARY_NAME + ";");
+            av2.visit("returnType", Type.getType("L" + PACKAGE_NAME + "/E;"));
+            av2.visit("presentAfter", new Integer(25));
+            av2.visitEnd();
+          }
+          {
+            AnnotationVisitor av2 = av1.visitAnnotation(null, "L" + CRT_BINARY_NAME + ";");
+            av2.visit("returnType", Type.getType("L" + PACKAGE_NAME + "/F;"));
+            av2.visit("presentAfter", new Integer(28));
+            av2.visitEnd();
+          }
+          av1.visitEnd();
+        }
+        av0.visitEnd();
+      }
+      mv.visitCode();
+      mv.visitTypeInsn(NEW, PACKAGE_NAME + "/F");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, PACKAGE_NAME + "/F", "<init>", "()V", false);
+      mv.visitInsn(ARETURN);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/Client.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/Client.java
index f2feffa..09a8513 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/Client.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/Client.java
@@ -5,15 +5,22 @@
 package com.android.tools.r8.ir.desugar.annotations.version3;
 
 import com.android.tools.r8.ir.desugar.annotations.A;
+import com.android.tools.r8.ir.desugar.annotations.D;
 
 public class Client {
   public static void main(String[] args) {
     A a = new A().method();
     B b = new B().method();
     C c = new C().method();
+    D d = ((D) new F()).method();
+    E e = ((E) new F()).method();
+    F f = new F().method();
 
     System.out.println("a=" + a.getClass().getSimpleName());
     System.out.println("b=" + b.getClass().getSimpleName());
     System.out.println("c=" + c.getClass().getSimpleName());
+    System.out.println("d=" + d.getClass().getSimpleName());
+    System.out.println("e=" + e.getClass().getSimpleName());
+    System.out.println("f=" + f.getClass().getSimpleName());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/ClientDump.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/ClientDump.java
index 5fea864..a509d96 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/ClientDump.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/ClientDump.java
@@ -55,6 +55,24 @@
       mv.visitMethodInsn(
           INVOKEVIRTUAL, PACKAGE_NAME + "/C", "method", "()L" + PACKAGE_NAME + "/C;", false);
       mv.visitVarInsn(ASTORE, 3);
+      mv.visitTypeInsn(NEW, PACKAGE_NAME + "/F");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, PACKAGE_NAME + "/F", "<init>", "()V", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, PACKAGE_NAME + "/D", "method", "()L" + PACKAGE_NAME + "/D;", false);
+      mv.visitVarInsn(ASTORE, 4);
+      mv.visitTypeInsn(NEW, PACKAGE_NAME + "/F");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, PACKAGE_NAME + "/F", "<init>", "()V", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, PACKAGE_NAME + "/E", "method", "()L" + PACKAGE_NAME + "/E;", false);
+      mv.visitVarInsn(ASTORE, 5);
+      mv.visitTypeInsn(NEW, PACKAGE_NAME + "/F");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, PACKAGE_NAME + "/F", "<init>", "()V", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, PACKAGE_NAME + "/F", "method", "()L" + PACKAGE_NAME + "/F;", false);
+      mv.visitVarInsn(ASTORE, 6);
       mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
       mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
       mv.visitInsn(DUP);
@@ -133,8 +151,86 @@
           INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
       mv.visitMethodInsn(
           INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+      mv.visitLdcInsn("d=");
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitVarInsn(ALOAD, 4);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/Class", "getSimpleName", "()Ljava/lang/String;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+      mv.visitLdcInsn("e=");
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitVarInsn(ALOAD, 5);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/Class", "getSimpleName", "()Ljava/lang/String;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+      mv.visitLdcInsn("f=");
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitVarInsn(ALOAD, 6);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/Class", "getSimpleName", "()Ljava/lang/String;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL,
+          "java/lang/StringBuilder",
+          "append",
+          "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+          false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
       mv.visitInsn(RETURN);
-      mv.visitMaxs(3, 4);
+      mv.visitMaxs(3, 7);
       mv.visitEnd();
     }
     cw.visitEnd();
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/E.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/E.java
new file mode 100644
index 0000000..bd48708
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/E.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations.version3;
+
+import com.android.tools.r8.ir.desugar.annotations.D;
+
+public abstract class E extends D {
+  @Override
+  public abstract E method();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/EDump.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/EDump.java
new file mode 100644
index 0000000..2572ae8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/EDump.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations.version3;
+
+import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.PACKAGE_NAME;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+// Generated by running tools/asmifier.py on build/classes/test/com/android/tools/r8/ir/desugar/
+// annotations/version3/E.class, and removing the subpackage "version3" from all class names.
+public class EDump implements Opcodes {
+  public static byte[] dump() throws Exception {
+
+    ClassWriter cw = new ClassWriter(0);
+    MethodVisitor mv;
+
+    cw.visit(
+        V1_8,
+        ACC_PUBLIC | ACC_SUPER | ACC_ABSTRACT,
+        "" + PACKAGE_NAME + "/E",
+        null,
+        "" + PACKAGE_NAME + "/D",
+        null);
+
+    {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "" + PACKAGE_NAME + "/D", "<init>", "()V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    {
+      mv =
+          cw.visitMethod(
+              ACC_PUBLIC | ACC_ABSTRACT, "method", "()L" + PACKAGE_NAME + "/E;", null, null);
+      mv.visitEnd();
+    }
+    {
+      mv =
+          cw.visitMethod(
+              ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC,
+              "method",
+              "()L" + PACKAGE_NAME + "/D;",
+              null,
+              null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, PACKAGE_NAME + "/E", "method", "()L" + PACKAGE_NAME + "/E;", false);
+      mv.visitInsn(ARETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/F.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/F.java
new file mode 100644
index 0000000..ec786fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/F.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations.version3;
+
+public class F extends E {
+  @Override
+  public F method() {
+    return new F();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/FDump.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/FDump.java
new file mode 100644
index 0000000..01d9d2c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/version3/FDump.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2023, 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.ir.desugar.annotations.version3;
+
+import static com.android.tools.r8.ir.desugar.annotations.CovariantReturnTypeAnnotationTransformerTest.PACKAGE_NAME;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+// Generated by running tools/asmifier.py on build/classes/test/com/android/tools/r8/ir/desugar/
+// annotations/version3/F.class, and removing the subpackage "version3" from all class names.
+public class FDump implements Opcodes {
+
+  public static byte[] dump() {
+
+    ClassWriter cw = new ClassWriter(0);
+    MethodVisitor mv;
+
+    cw.visit(V1_8, ACC_PUBLIC | ACC_SUPER, PACKAGE_NAME + "/F", null, PACKAGE_NAME + "/E", null);
+
+    {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, PACKAGE_NAME + "/E", "<init>", "()V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    {
+      mv = cw.visitMethod(ACC_PUBLIC, "method", "()L" + PACKAGE_NAME + "/F;", null, null);
+      mv.visitCode();
+      mv.visitTypeInsn(NEW, PACKAGE_NAME + "/F");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, PACKAGE_NAME + "/F", "<init>", "()V", false);
+      mv.visitInsn(ARETURN);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+    }
+    {
+      mv =
+          cw.visitMethod(
+              ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC,
+              "method",
+              "()L" + PACKAGE_NAME + "/E;",
+              null,
+              null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, PACKAGE_NAME + "/F", "method", "()L" + PACKAGE_NAME + "/F;", false);
+      mv.visitInsn(ARETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    {
+      mv =
+          cw.visitMethod(
+              ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC,
+              "method",
+              "()L" + PACKAGE_NAME + "/D;",
+              null,
+              null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, PACKAGE_NAME + "/F", "method", "()L" + PACKAGE_NAME + "/F;", false);
+      mv.visitInsn(ARETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+}