Add a copy of all pattern matching switch tests for JDK-23

Code generation of the bootstrap method signature changed.

Bug: b/382880986
Change-Id: I116b9640f051aae3ecd1b2b62a16e03c234014e9
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
index f9d6bc9..63c6d26 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
@@ -24,6 +24,8 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+// This test is copied into later JDK tests (currently JDK-23). The reason for the copy is that
+// from JDK-23 the code generation changed. Please update the copy as well if updating this test.
 @RunWith(Parameterized.class)
 public class EnumLessCasesAtRuntimeSwitchTest extends TestBase {
 
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
index 77a6e53..575ebfd 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
@@ -26,6 +26,8 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+// This test is copied into later JDK tests (currently JDK-23). The reason for the copy is that
+// from JDK-23 the code generation changed. Please update the copy as well if updating this test.
 @RunWith(Parameterized.class)
 public class EnumMoreCasesAtRuntimeSwitchTest extends TestBase {
 
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
index bd16f68..5b1f9fb 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumSwitchTest.java
@@ -25,6 +25,8 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+// This test is copied into later JDK tests (currently JDK-23). The reason for the copy is that
+// from JDK-23 the code generation changed. Please update the copy as well if updating this test.
 @RunWith(Parameterized.class)
 public class EnumSwitchTest extends TestBase {
 
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java b/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java
similarity index 94%
rename from src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java
rename to src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java
index 095620c..3b9fe1b 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java
+++ b/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java
@@ -22,8 +22,10 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+// This test is copied into later JDK tests (currently JDK-23). The reason for the copy is that
+// from JDK-23 the code generation changed. Please update the copy as well if updating this test.
 @RunWith(Parameterized.class)
-public class EnumSwitchUsingEnumSwitchBootstrapMethod extends TestBase {
+public class EnumSwitchUsingEnumSwitchBootstrapMethodTest extends TestBase {
 
   @Parameter public TestParameters parameters;
 
diff --git a/src/test/examplesJava21/switchpatternmatching/TypeSwitchMissingClassTest.java b/src/test/examplesJava21/switchpatternmatching/TypeSwitchMissingClassTest.java
index cd2e035..c037f90 100644
--- a/src/test/examplesJava21/switchpatternmatching/TypeSwitchMissingClassTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/TypeSwitchMissingClassTest.java
@@ -24,6 +24,8 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+// This test is copied into later JDK tests (currently JDK-23). The reason for the copy is that
+// from JDK-23 the code generation changed. Please update the copy as well if updating this test.
 @RunWith(Parameterized.class)
 public class TypeSwitchMissingClassTest extends TestBase {
 
diff --git a/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java b/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
index 19cee5b..83a428f 100644
--- a/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
+++ b/src/test/examplesJava21/switchpatternmatching/TypeSwitchTest.java
@@ -22,6 +22,8 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+// This test is copied into later JDK tests (currently JDK-23). The reason for the copy is that
+// from JDK-23 the code generation changed. Please update the copy as well if updating this test.
 @RunWith(Parameterized.class)
 public class TypeSwitchTest extends TestBase {
 
diff --git a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
new file mode 100644
index 0000000..e367e8a
--- /dev/null
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
@@ -0,0 +1,206 @@
+// 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.java23.switchpatternmatching;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.JdkClassFileProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Assume;
+import org.junit.Ignore;
+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;
+
+// This is a copy of the same test from JDK-21. The reason for the copy is that from JDK-23 the
+// code generation for pattern matching switch changed (the bootstrap method signature used in the
+// invokedynamic changed).
+@RunWith(Parameterized.class)
+public class EnumLessCasesAtRuntimeSwitchTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public static String EXPECTED_OUTPUT =
+      StringUtils.lines("TYPE", "null", "E1", "E3", "E5", "a C", "ENUM", "null", "1", "3", "0");
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
+    assertTrue(
+        hasJdk21EnumSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("enumSwitch")));
+    assertTrue(
+        hasJdk21TypeSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("typeSwitch")));
+
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::addModifiedProgramClasses)
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+            r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+  }
+
+  private <T extends TestBuilder<?, T>> void addModifiedProgramClasses(
+      TestBuilder<?, T> testBuilder) throws Exception {
+    testBuilder
+        .addStrippedOuter(getClass())
+        .addProgramClasses(FakeI.class, C.class, I.class, Main.class)
+        .addProgramClassFileData(
+            transformer(CompileTimeE.class)
+                .setImplements(FakeI.class)
+                .setClassDescriptor(RuntimeE.class.descriptorString())
+                .transform())
+        .addProgramClassFileData(
+            transformer(RuntimeE.class)
+                .setImplements(I.class)
+                .setClassDescriptor(CompileTimeE.class.descriptorString())
+                .transform());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)
+        && parameters.getApiLevel().isLessThan(AndroidApiLevel.BAKLAVA)) {
+      assertThrows(
+          CompilationFailedException.class,
+          () ->
+              testForD8(parameters.getBackend())
+                  .apply(this::addModifiedProgramClasses)
+                  .setMinApi(parameters)
+                  .compileWithExpectedDiagnostics(
+                      diagnostics -> {
+                        diagnostics.assertOnlyErrors();
+                        diagnostics.assertErrorsMatch(
+                            diagnosticMessage(
+                                containsString("DexValueConstDynamic should be desugared")));
+                      }));
+    } else {
+      testForD8(parameters.getBackend())
+          .apply(this::addModifiedProgramClasses)
+          .setMinApi(parameters)
+          .run(parameters.getRuntime(), Main.class)
+          .assertFailure();
+    }
+  }
+
+  @Test
+  @Ignore("TODO(b/382880986) enable test when fixed.")
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    Assume.assumeTrue(
+        parameters.isDexRuntime()
+            || (parameters.isCfRuntime()
+                && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21)));
+    testForR8(parameters.getBackend())
+        .apply(this::addModifiedProgramClasses)
+        .applyIf(
+            parameters.isCfRuntime(),
+            b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
+        .setMinApi(parameters)
+        .addKeepMainRule(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  sealed interface I permits CompileTimeE, C {}
+
+  public enum CompileTimeE implements I {
+    E1,
+    E2, // Case missing at runtime.
+    E3,
+    E4, // Case missing at runtime.
+    E5
+  }
+
+  interface FakeI {}
+
+  public enum RuntimeE implements FakeI {
+    E1,
+    E3,
+    E5
+  }
+
+  static final class C implements I {}
+
+  static class Main {
+
+    static void typeSwitch(I i) {
+      switch (i) {
+        case CompileTimeE.E1 -> {
+          System.out.println("E1");
+        }
+        case CompileTimeE.E2 -> { // Case missing at runtime.
+          System.out.println("E2");
+        }
+        case CompileTimeE.E3 -> {
+          System.out.println("E3");
+        }
+        case CompileTimeE.E4 -> { // Case missing at runtime.
+          System.out.println("E4");
+        }
+        case CompileTimeE.E5 -> {
+          System.out.println("E5");
+        }
+        case C c -> {
+          System.out.println("a C");
+        }
+      }
+    }
+
+    static void enumSwitch(CompileTimeE e) {
+      switch (e) {
+        case null -> System.out.println("null");
+        case CompileTimeE.E1 -> System.out.println("1");
+        case CompileTimeE.E2 -> System.out.println("2"); // Case missing at runtime.
+        case CompileTimeE t when t == CompileTimeE.E3 -> System.out.println("3");
+        case CompileTimeE t when t.name().equals("E4") ->
+            System.out.println("4"); // Case missing at runtime.
+        case CompileTimeE t -> System.out.println("0");
+      }
+    }
+
+    public static void main(String[] args) {
+      System.out.println("TYPE");
+      try {
+        typeSwitch(null);
+      } catch (NullPointerException e) {
+        System.out.println("null");
+      }
+      typeSwitch(CompileTimeE.E1);
+      typeSwitch(CompileTimeE.E3);
+      typeSwitch(CompileTimeE.E5);
+      typeSwitch(new C());
+
+      System.out.println("ENUM");
+      enumSwitch(null);
+      enumSwitch(CompileTimeE.E1);
+      enumSwitch(CompileTimeE.E3);
+      enumSwitch(CompileTimeE.E5);
+    }
+  }
+}
diff --git a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
new file mode 100644
index 0000000..8b3f44d
--- /dev/null
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
@@ -0,0 +1,282 @@
+// 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.java23.switchpatternmatching;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.matchException;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.JdkClassFileProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Assume;
+import org.junit.Ignore;
+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;
+
+// This is a copy of the same test from JDK-21. The reason for the copy is that from JDK-23 the
+// code generation for pattern matching switch changed (the bootstrap method signature used in the
+// invokedynamic changed).
+@RunWith(Parameterized.class)
+public class EnumMoreCasesAtRuntimeSwitchTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public static String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "TYPE",
+          "null",
+          "E1",
+          "class %s",
+          "E3",
+          "class %s",
+          "E5",
+          "a C",
+          "ENUM",
+          "null",
+          "1",
+          "0",
+          "3",
+          "4",
+          "0");
+
+  public static String UNEXPECTED_OUTPUT_R8_DEX =
+      StringUtils.lines(
+          "TYPE",
+          "null",
+          "E1",
+          "class %s",
+          "E3",
+          "class %s",
+          "E5",
+          "a C",
+          "ENUM",
+          "null",
+          "1",
+          "0",
+          "3",
+          "0", // This is the difference from the EXPECTED_OUTPUT.
+          "0");
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
+    assertTrue(
+        hasJdk21EnumSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("enumSwitch")));
+    assertTrue(
+        hasJdk21TypeSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("typeSwitch")));
+
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::addModifiedProgramClasses)
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23),
+            r ->
+                r.assertSuccessWithOutput(
+                    String.format(EXPECTED_OUTPUT, matchException(), matchException())),
+            r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+  }
+
+  private <T extends TestBuilder<?, T>> void addModifiedProgramClasses(
+      TestBuilder<?, T> testBuilder) throws Exception {
+    testBuilder
+        .addStrippedOuter(getClass())
+        .addProgramClasses(FakeI.class, C.class, I.class)
+        .addProgramClassFileData(
+            transformer(CompileTimeE.class)
+                .setImplements(FakeI.class)
+                .setClassDescriptor(RuntimeE.class.descriptorString())
+                .transform())
+        .addProgramClassFileData(
+            transformer(RuntimeE.class)
+                .setImplements(I.class)
+                .setClassDescriptor(CompileTimeE.class.descriptorString())
+                .transform())
+        .addProgramClassFileData(
+            transformer(Main.class)
+                .transformFieldInsnInMethod(
+                    "getE2",
+                    (opcode, owner, name, descriptor, visitor) -> {
+                      visitor.visitFieldInsn(opcode, owner, "E2", descriptor);
+                    })
+                .transformFieldInsnInMethod(
+                    "getE4",
+                    (opcode, owner, name, descriptor, visitor) -> {
+                      visitor.visitFieldInsn(opcode, owner, "E4", descriptor);
+                    })
+                .transform());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)
+        && parameters.getApiLevel().isLessThan(AndroidApiLevel.BAKLAVA)) {
+      assertThrows(
+          CompilationFailedException.class,
+          () ->
+              testForD8(parameters.getBackend())
+                  .apply(this::addModifiedProgramClasses)
+                  .setMinApi(parameters)
+                  .compileWithExpectedDiagnostics(
+                      diagnostics -> {
+                        diagnostics.assertOnlyErrors();
+                        diagnostics.assertErrorsMatch(
+                            diagnosticMessage(
+                                containsString("DexValueConstDynamic should be desugared")));
+                      }));
+    } else {
+      testForD8(parameters.getBackend())
+          .apply(this::addModifiedProgramClasses)
+          .setMinApi(parameters)
+          .run(parameters.getRuntime(), Main.class)
+          .assertFailure();
+    }
+  }
+
+  @Test
+  @Ignore("TODO(b/382880986) enable test when fixed.")
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    Assume.assumeTrue(
+        parameters.isDexRuntime()
+            || (parameters.isCfRuntime()
+                && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21)));
+    testForR8(parameters.getBackend())
+        .apply(this::addModifiedProgramClasses)
+        .applyIf(
+            parameters.isCfRuntime(),
+            b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
+        .setMinApi(parameters)
+        .addKeepMainRule(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.isDexRuntime(),
+            // TODO(b/381825147): Should same output.
+            r ->
+                r.assertSuccessWithOutput(
+                    String.format(
+                        UNEXPECTED_OUTPUT_R8_DEX,
+                        matchException(parameters),
+                        matchException(parameters))),
+            r ->
+                r.assertSuccessWithOutput(
+                    String.format(
+                        EXPECTED_OUTPUT, matchException(parameters), matchException(parameters))));
+  }
+
+  sealed interface I permits CompileTimeE, C {}
+
+  public enum RuntimeE implements FakeI {
+    E1,
+    E2, // Case present at runtime.
+    E3,
+    E4, // Case present at runtime.
+    E5
+  }
+
+  interface FakeI {}
+
+  public enum CompileTimeE implements I {
+    E1,
+    E3,
+    E5
+  }
+
+  static final class C implements I {}
+
+  static class Main {
+
+    static void typeSwitch(I i) {
+      switch (i) {
+        case CompileTimeE.E1 -> {
+          System.out.println("E1");
+        }
+        case CompileTimeE.E3 -> {
+          System.out.println("E3");
+        }
+        case CompileTimeE.E5 -> {
+          System.out.println("E5");
+        }
+        case C c -> {
+          System.out.println("a C");
+        }
+      }
+    }
+
+    static void enumSwitch(CompileTimeE e) {
+      switch (e) {
+        case null -> System.out.println("null");
+        case CompileTimeE.E1 -> System.out.println("1");
+        case CompileTimeE t when t == CompileTimeE.E3 -> System.out.println("3");
+        case CompileTimeE t when t.name().equals("E4") ->
+            System.out.println("4"); // Case present at runtime.
+        case CompileTimeE t -> System.out.println("0");
+      }
+    }
+
+    public static CompileTimeE getE2() {
+      // Replaced by RuntimeE.E2;
+      return CompileTimeE.E1;
+    }
+
+    public static CompileTimeE getE4() {
+      // Replaced by RuntimeE.E4;
+      return CompileTimeE.E1;
+    }
+
+    public static void main(String[] args) {
+      System.out.println("TYPE");
+      try {
+        typeSwitch(null);
+      } catch (NullPointerException e) {
+        System.out.println("null");
+      }
+      typeSwitch(CompileTimeE.E1);
+      try {
+        typeSwitch(getE2());
+      } catch (Exception e) {
+        System.out.println(e.getClass().toString());
+      }
+      typeSwitch(CompileTimeE.E3);
+      try {
+        typeSwitch(getE4());
+      } catch (Exception e) {
+        System.out.println(e.getClass().toString());
+      }
+      typeSwitch(CompileTimeE.E5);
+      typeSwitch(new C());
+
+      System.out.println("ENUM");
+      enumSwitch(null);
+      enumSwitch(CompileTimeE.E1);
+      enumSwitch(getE2());
+      enumSwitch(CompileTimeE.E3);
+      enumSwitch(getE4());
+      enumSwitch(CompileTimeE.E5);
+    }
+  }
+}
diff --git a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumSwitchTest.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumSwitchTest.java
new file mode 100644
index 0000000..ddf16a8
--- /dev/null
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumSwitchTest.java
@@ -0,0 +1,203 @@
+// 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.java23.switchpatternmatching;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.matchException;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.JdkClassFileProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Assume;
+import org.junit.Ignore;
+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;
+
+// This is a copy of the same test from JDK-21. The reason for the copy is that from JDK-23 the
+// code generation for pattern matching switch changed (the bootstrap method signature used in the
+// invokedynamic changed).
+@RunWith(Parameterized.class)
+public class EnumSwitchTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public static String EXPECTED_OUTPUT =
+      StringUtils.lines("null", "E1", "E2", "E3", "E4", "a C", "class %s");
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
+    assertTrue(
+        hasJdk21TypeSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("enumSwitch")));
+
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::addModifiedProgramClasses)
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23),
+            r -> r.assertSuccessWithOutput(String.format(EXPECTED_OUTPUT, matchException())),
+            r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+  }
+
+  private <T extends TestBuilder<?, T>> void addModifiedProgramClasses(
+      TestBuilder<?, T> testBuilder) throws Exception {
+    testBuilder
+        .addStrippedOuter(getClass())
+        .addProgramClasses(FakeI.class, E.class, C.class)
+        .addProgramClassFileData(
+            transformer(I.class)
+                .setPermittedSubclasses(I.class, E.class, C.class, D.class)
+                .transform())
+        .addProgramClassFileData(transformer(D.class).setImplements(I.class).transform())
+        .addProgramClassFileData(
+            transformer(Main.class)
+                .transformTypeInsnInMethod(
+                    "getD",
+                    (opcode, type, visitor) ->
+                        visitor.visitTypeInsn(opcode, "switchpatternmatching/EnumSwitchTest$D"))
+                .transformMethodInsnInMethod(
+                    "getD",
+                    (opcode, owner, name, descriptor, isInterface, visitor) -> {
+                      assert name.equals("<init>");
+                      visitor.visitMethodInsn(
+                          opcode,
+                          "switchpatternmatching/EnumSwitchTest$D",
+                          name,
+                          descriptor,
+                          isInterface);
+                    })
+                .transform());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)
+        && parameters.getApiLevel().isLessThan(AndroidApiLevel.BAKLAVA)) {
+      assertThrows(
+          CompilationFailedException.class,
+          () ->
+              testForD8(parameters.getBackend())
+                  .apply(this::addModifiedProgramClasses)
+                  .setMinApi(parameters)
+                  .compileWithExpectedDiagnostics(
+                      diagnostics -> {
+                        diagnostics.assertOnlyErrors();
+                        diagnostics.assertErrorsMatch(
+                            diagnosticMessage(
+                                containsString("DexValueConstDynamic should be desugared")));
+                      }));
+    } else {
+      testForD8(parameters.getBackend())
+          .apply(this::addModifiedProgramClasses)
+          .setMinApi(parameters)
+          .run(parameters.getRuntime(), Main.class)
+          .assertFailure();
+    }
+  }
+
+  @Test
+  @Ignore("TODO(b/382880986) enable test when fixed.")
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    Assume.assumeTrue(
+        parameters.isDexRuntime()
+            || (parameters.isCfRuntime()
+                && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21)));
+    testForR8(parameters.getBackend())
+        .apply(this::addModifiedProgramClasses)
+        .applyIf(
+            parameters.isCfRuntime(),
+            b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
+        .setMinApi(parameters)
+        .addKeepMainRule(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(String.format(EXPECTED_OUTPUT, matchException(parameters)));
+  }
+
+  // D is added to the list of permitted subclasses to reproduce the MatchException.
+  sealed interface I permits E, C {}
+
+  interface FakeI {}
+
+  public enum E implements I {
+    E1,
+    E2,
+    E3,
+    E4
+  }
+
+  // Replaced with I.
+  static final class D implements FakeI {}
+
+  static final class C implements I {}
+
+  static class Main {
+
+    static void enumSwitch(I i) {
+      switch (i) {
+        case E.E1 -> {
+          System.out.println("E1");
+        }
+        case E.E2 -> {
+          System.out.println("E2");
+        }
+        case E.E3 -> {
+          System.out.println("E3");
+        }
+        case E.E4 -> {
+          System.out.println("E4");
+        }
+        case C c -> {
+          System.out.println("a C");
+        }
+      }
+    }
+
+    public static void main(String[] args) {
+      try {
+        enumSwitch(null);
+      } catch (NullPointerException e) {
+        System.out.println("null");
+      }
+      enumSwitch(E.E1);
+      enumSwitch(E.E2);
+      enumSwitch(E.E3);
+      enumSwitch(E.E4);
+      enumSwitch(new C());
+      try {
+        enumSwitch(getD());
+      } catch (Throwable t) {
+        System.out.println(t.getClass());
+      }
+    }
+
+    public static I getD() {
+      // Replaced by new D();
+      return new C();
+    }
+  }
+}
diff --git a/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java
similarity index 91%
copy from src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java
copy to src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java
index 095620c..4f68404 100644
--- a/src/test/examplesJava21/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethod.java
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java
@@ -1,7 +1,7 @@
 // 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 switchpatternmatching;
+package com.android.tools.r8.java23.switchpatternmatching;
 
 import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch;
 import static org.junit.Assert.assertTrue;
@@ -22,8 +22,11 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+// This is a copy of the same test from JDK-21. The reason for the copy is that from JDK-23 the
+// code generation for pattern matching switch changed (the bootstrap method signature used in the
+// invokedynamic changed).
 @RunWith(Parameterized.class)
-public class EnumSwitchUsingEnumSwitchBootstrapMethod extends TestBase {
+public class EnumSwitchUsingEnumSwitchBootstrapMethodTest extends TestBase {
 
   @Parameter public TestParameters parameters;
 
@@ -64,7 +67,7 @@
         .addInnerClassesAndStrippedOuter(getClass())
         .run(parameters.getRuntime(), Main.class)
         .applyIf(
-            parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21),
+            parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23),
             r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
             r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
   }
@@ -85,7 +88,7 @@
     Assume.assumeTrue(
         parameters.isDexRuntime()
             || (parameters.isCfRuntime()
-                && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21)));
+                && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23)));
     testForR8(parameters.getBackend())
         .addInnerClassesAndStrippedOuter(getClass())
         .applyIf(
diff --git a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchTest.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchTest.java
index 1f0a294..38f7007 100644
--- a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchTest.java
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/StringSwitchTest.java
@@ -27,7 +27,8 @@
 import org.junit.runners.Parameterized.Parameters;
 
 // This is a copy of the same test from JDK-21. The reason for the copy is that from JDK-23 the
-// code generation changed.
+// code generation for pattern matching switch changed (the bootstrap method signature used in the
+// invokedynamic changed).
 @RunWith(Parameterized.class)
 public class StringSwitchTest extends TestBase {
 
diff --git a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/TypeSwitchMissingClassTest.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/TypeSwitchMissingClassTest.java
new file mode 100644
index 0000000..8bb22d0
--- /dev/null
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/TypeSwitchMissingClassTest.java
@@ -0,0 +1,162 @@
+// 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.java23.switchpatternmatching;
+
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.JdkClassFileProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.List;
+import org.junit.Assume;
+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;
+
+// This is a copy of the same test from JDK-21. The reason for the copy is that from JDK-23 the
+// code generation for pattern matching switch changed (the bootstrap method signature used in the
+// invokedynamic changed).
+@RunWith(Parameterized.class)
+public class TypeSwitchMissingClassTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public ClassHolder present;
+
+  @Parameters(name = "{0}, {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+        List.of(new ClassHolder(C.class), new ClassHolder(Color.class)));
+  }
+
+  // ClassHolder allows to correctly print parameters in the IntelliJ test IDE with {1}.
+  private static class ClassHolder {
+    public final Class<?> clazz;
+
+    private ClassHolder(Class<?> clazz) {
+      this.clazz = clazz;
+    }
+
+    @Override
+    public String toString() {
+      return clazz.getSimpleName();
+    }
+  }
+
+  public static String EXPECTED_OUTPUT =
+      StringUtils.lines("null", "String", "Array of int, length = 0", "Other");
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
+    assertTrue(
+        hasJdk21TypeSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("typeSwitch")));
+
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::addModifiedProgramClasses)
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23),
+            this::assertResult,
+            r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+  }
+
+  private void assertResult(TestRunResult<?> r) {
+    if (present.clazz.equals(C.class)) {
+      r.assertSuccessWithOutput(EXPECTED_OUTPUT);
+    } else {
+      r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+    }
+  }
+
+  private <T extends TestBuilder<?, T>> void addModifiedProgramClasses(
+      TestBuilder<?, T> testBuilder) throws Exception {
+    testBuilder
+        .addProgramClassFileData(transformer(Main.class).clearNest().transform())
+        .addProgramClassFileData(transformer(present.clazz).clearNest().transform());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .apply(this::addModifiedProgramClasses)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::assertResult);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    Assume.assumeTrue(
+        parameters.isDexRuntime()
+            || (parameters.isCfRuntime()
+                && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23)));
+    testForR8(parameters.getBackend())
+        .apply(this::addModifiedProgramClasses)
+        .applyIf(
+            parameters.isCfRuntime(),
+            b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
+        .addIgnoreWarnings(present.clazz.equals(Color.class))
+        .allowDiagnosticWarningMessages(present.clazz.equals(Color.class))
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::assertResult);
+  }
+
+  // Enum will be missing at runtime.
+  enum Color {
+    RED,
+    GREEN,
+    BLUE;
+  }
+
+  // Class will be missing at runtime.
+  static class C {
+
+    @Override
+    public String toString() {
+      return "CCC";
+    }
+  }
+
+  static class Main {
+
+    static void typeSwitch(Object obj) {
+      switch (obj) {
+        case null -> System.out.println("null");
+        case Color.RED -> System.out.println("RED!!!");
+        case Color.BLUE -> System.out.println("BLUE!!!");
+        case Color.GREEN -> System.out.println("GREEN!!!");
+        case String string -> System.out.println("String");
+        case C c -> System.out.println(c.toString() + "!!!");
+        case int[] intArray -> System.out.println("Array of int, length = " + intArray.length);
+        default -> System.out.println("Other");
+      }
+    }
+
+    public static void main(String[] args) {
+      typeSwitch(null);
+      typeSwitch("s");
+      typeSwitch(new int[] {});
+      typeSwitch(new Object());
+    }
+  }
+}
diff --git a/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/TypeSwitchTest.java b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/TypeSwitchTest.java
new file mode 100644
index 0000000..05584bc
--- /dev/null
+++ b/src/test/java23/com/android/tools/r8/java23/switchpatternmatching/TypeSwitchTest.java
@@ -0,0 +1,120 @@
+// 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.java23.switchpatternmatching;
+
+import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.JdkClassFileProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Assume;
+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;
+
+// This is a copy of the same test from JDK-21. The reason for the copy is that from JDK-23 the
+// code generation for pattern matching switch changed (the bootstrap method signature used in the
+// invokedynamic changed).
+@RunWith(Parameterized.class)
+public class TypeSwitchTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public static String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "null", "String", "Color: RED", "Point: [0;0]", "Array of int, length = 0", "Other");
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    CodeInspector inspector = new CodeInspector(ToolHelper.getClassFileForTestClass(Main.class));
+    assertTrue(
+        hasJdk21TypeSwitch(inspector.clazz(Main.class).uniqueMethodWithOriginalName("typeSwitch")));
+
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .addInnerClassesAndStrippedOuter(getClass())
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+            r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .addInnerClassesAndStrippedOuter(getClass())
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            isRecordsFullyDesugaredForD8(parameters)
+                || runtimeWithRecordsSupport(parameters.getRuntime()),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+            r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    Assume.assumeTrue(
+        parameters.isDexRuntime()
+            || (parameters.isCfRuntime()
+                && parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK23)));
+    testForR8(parameters.getBackend())
+        .addInnerClassesAndStrippedOuter(getClass())
+        .applyIf(
+            parameters.isCfRuntime(),
+            b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  record Point(int i, int j) {}
+
+  enum Color {
+    RED,
+    GREEN,
+    BLUE;
+  }
+
+  static class Main {
+
+    static void typeSwitch(Object obj) {
+      switch (obj) {
+        case null -> System.out.println("null");
+        case String string -> System.out.println("String");
+        case Color color -> System.out.println("Color: " + color);
+        case Point point -> System.out.println("Point: [" + point.i + ";" + point.j + "]");
+        case int[] intArray -> System.out.println("Array of int, length = " + intArray.length);
+        default -> System.out.println("Other");
+      }
+    }
+
+    public static void main(String[] args) {
+      typeSwitch(null);
+      typeSwitch("s");
+      typeSwitch(Color.RED);
+      typeSwitch(new Point(0, 0));
+      typeSwitch(new int[] {});
+      typeSwitch(new Object());
+    }
+  }
+}