RFR: 8300543 Compiler Implementation for Pattern Matching for switch

Christian Wimmer cwimmer at openjdk.org
Fri Apr 14 12:56:53 UTC 2023


On Fri, 17 Mar 2023 12:15:58 GMT, Jan Lahoda <jlahoda at openjdk.org> wrote:

> This is the first draft of a patch for JEP 440 and JEP 441. Changes included:
> 
>  - the pattern matching for switch and record patterns features are made final, together with updates to tests.
>  - parenthesized patterns are removed.
>  - qualified enum constants are supported for case labels.
> 
> This change herein also includes removal record patterns in for each loop, which may be split into a separate PR in the future.

src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java line 1:

> 1: /*

@lahodaj  The way that the boostrap method `enumSwitch` and its corresponding implementation method `doEnumSwitch` are implemented right now has an unfortunate side effect on class initialization: since the bootstrap method already looks up the actual enum values, it triggers class initialization of the enum. This is not a problem when the switched value is non-null, because then obviously the enum is already initialized. But when the switched value is `null`, then the boostrap method triggers class initialization.

Example:

enum E { 
    A, B;

    static {
        new Throwable("Hello, World!").printStackTrace();
    }
}


public class SwitchTest {

    public static int testMethod(E selExpr) {
        return switch (selExpr) {
            case A -> 3;
            case B -> 6;
            case null -> -1;
        };
    }

    public static void main(String argv[]) {
        System.out.println(testMethod(null));
    }
}


It produces the following output (on JDK 20, but I don't see any changes in this PR that would alter the behavior):

java.lang.Throwable: Hello, World!
	at E.<clinit>(SwitchTest.java:5)
	at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
	at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1160)
	at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:300)
	at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newMethodAccessor(MethodHandleAccessorFactory.java:71)
	at java.base/jdk.internal.reflect.ReflectionFactory.newMethodAccessor(ReflectionFactory.java:159)
	at java.base/java.lang.reflect.Method.acquireMethodAccessor(Method.java:724)
	at java.base/java.lang.reflect.Method.invoke(Method.java:575)
	at java.base/java.lang.Class.getEnumConstantsShared(Class.java:3938)
	at java.base/java.lang.Class.enumConstantDirectory(Class.java:3961)
	at java.base/java.lang.Enum.valueOf(Enum.java:268)
	at java.base/java.lang.invoke.ConstantBootstraps.enumConstant(ConstantBootstraps.java:144)
	at java.base/java.lang.runtime.SwitchBootstraps.convertEnumConstants(SwitchBootstraps.java:262)
	at java.base/java.lang.runtime.SwitchBootstraps.lambda$enumSwitch$0(SwitchBootstraps.java:238)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1006)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
	at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
	at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
	at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
	at java.base/java.lang.runtime.SwitchBootstraps.enumSwitch(SwitchBootstraps.java:238)
	at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:147)
	at java.base/java.lang.invoke.CallSite.makeSite(CallSite.java:316)
	at java.base/java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:279)
	at java.base/java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:269)
	at SwitchTest.testMethod(SwitchTest.java:13)
	at SwitchTest.main(SwitchTest.java:21)
-1

I argue this is bad because in general because every unnecessary class initialization (and its side effects) should be avoided.

But it is really a problem for any kind of eager-bootstrapping system, like Project Leyden or GraalVM Native Image: if it is not possible to execute the bootstrap methods early due to possible side effects, optimizations are quite limited and the bootstrap method needs to be executed at run time.

A simple solution would be to keep the elements in the data array as `String` instead of enum elements, and in `doEnumSwitch` do a `== target.name` (the method has the comment `// Dumbest possible strategy` anyways).

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/13074#discussion_r1164570743


More information about the core-libs-dev mailing list