Skip to content

Optimize level 9 breaks java 14 Type myVar = switch expressions compilationΒ #10121

Closed

Description

@JeffreyDevloo

The optimize flag for the GWT compiler changes results in compilation error of this switch expression

public class TestComposite extends Composite {
    private final TextBox textBox = new TextBox();

    public TestComposite() {
        super();
    }

    void setText(TestEnum testEnum) {
        Void ignored = switch (testEnum) {
            case A -> {
                textBox.setText("A");
                yield null;
            }
            case B -> {
                textBox.setText("B");
                yield null;
            }
        };
    }

    enum TestEnum {
        A, B
    }
}

This results in IllegalStateException("top-level switch expr") coming from the GenerateJavaScriptAST#transformExpressionStatement when running with optimize level 9.

[INFO] com.google.gwt.dev.jjs.InternalCompilerException: Unexpected error during visit.
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.translateException(JVisitor.java:111)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:130)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer.transformIntoExcludingNulls(JTransformer.java:1120)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformBlock(GenerateJavaScriptAST.java:583)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformBlock(GenerateJavaScriptAST.java:520)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:647)
[INFO]  at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:93)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transform(GenerateJavaScriptAST.java:2672)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethodBody(GenerateJavaScriptAST.java:833)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethodBody(GenerateJavaScriptAST.java:520)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:854)
[INFO]  at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:81)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethod(GenerateJavaScriptAST.java:804)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformMethod(GenerateJavaScriptAST.java:520)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:848)
[INFO]  at com.google.gwt.dev.jjs.ast.JMethod.traverse(JMethod.java:777)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.emitStaticMethods(GenerateJavaScriptAST.java:1869)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformDeclaredType(GenerateJavaScriptAST.java:647)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformDeclaredType(GenerateJavaScriptAST.java:520)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer.transformClassType(JTransformer.java:95)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:695)
[INFO]  at com.google.gwt.dev.jjs.ast.JClassType.traverse(JClassType.java:145)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformProgram(GenerateJavaScriptAST.java:1242)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformProgram(GenerateJavaScriptAST.java:520)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:943)
[INFO]  at com.google.gwt.dev.jjs.ast.JProgram.traverse(JProgram.java:1248)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer.transform(JTransformer.java:1100)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST.execImpl(GenerateJavaScriptAST.java:3171)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST.exec(GenerateJavaScriptAST.java:2944)
[INFO]  at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:380)
[INFO]  at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:274)
[INFO]  at com.google.gwt.dev.CompilePerms.compile(CompilePerms.java:198)
[INFO]  at com.google.gwt.dev.ThreadedPermutationWorkerFactory$ThreadedPermutationWorker.compile(ThreadedPermutationWorkerFactory.java:50)
[INFO]  at com.google.gwt.dev.PermutationWorkerFactory$Manager$WorkerThread.run(PermutationWorkerFactory.java:74)
[INFO]  at java.base/java.lang.Thread.run(Thread.java:1583)
[INFO] Caused by: java.lang.IllegalStateException: top-level switch expr
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformExpressionStatement(GenerateJavaScriptAST.java:708)
[INFO]  at com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST$GenerateJavaScriptTransformer.transformExpressionStatement(GenerateJavaScriptAST.java:520)
[INFO]  at com.google.gwt.dev.jjs.ast.JTransformer$JRewriterVisitor.visit(JTransformer.java:755)
[INFO]  at com.google.gwt.dev.jjs.ast.JExpressionStatement.traverse(JExpressionStatement.java:41)
[INFO]  at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
[INFO]  ... 49 more
[INFO]          [ERROR] at TestComposite.java(14): switch (0)  {
[INFO]   case 0: 
[INFO]   {
[INFO]     ValueBoxBase.$setText(this$static.textBox, "A");
[INFO]     yield  null;
[INFO]   }
[INFO]   case 1: 
[INFO]   {
[INFO]     ValueBoxBase.$setText(this$static.textBox, "B");
[INFO]     yield  null;
[INFO]   }
[INFO] }
[INFO]             com.google.gwt.dev.jjs.ast.JExpressionStatement
[INFO]          [ERROR] at TestComposite.java(13): {
[INFO]   switch (0)  {
[INFO]     case 0: 
[INFO]     {
[INFO]       ValueBoxBase.$setText(this$static.textBox, "A");
[INFO]       yield  null;
[INFO]     }
[INFO]     case 1: 
[INFO]     {
[INFO]       ValueBoxBase.$setText(this$static.textBox, "B");
[INFO]       yield  null;
[INFO]     }
[INFO]   }
[INFO] }
[INFO]             com.google.gwt.dev.jjs.ast.JBlock
[INFO]          [ERROR] at TestComposite.java(13): {
[INFO]   switch (0)  {
[INFO]     case 0: 
[INFO]     {
[INFO]       ValueBoxBase.$setText(this$static.textBox, "A");
[INFO]       yield  null;
[INFO]     }
[INFO]     case 1: 
[INFO]     {
[INFO]       ValueBoxBase.$setText(this$static.textBox, "B");
[INFO]       yield  null;
[INFO]     }
[INFO]   }
[INFO] }
[INFO]             com.google.gwt.dev.jjs.ast.JMethodBody
[INFO]          [ERROR] at TestComposite.java(13): com.example.client.editor.TestComposite.$setText(Lcom/example/client/editor/TestComposite;Lcom/example/client/editor/TestComposite$TestEnum;)V
[INFO]             com.google.gwt.dev.jjs.ast.JMethod
[INFO]          [ERROR] at TestComposite.java(6): com.example.client.editor.TestComposite (extends Composite)
[INFO]             com.google.gwt.dev.jjs.ast.JClassType
[INFO]          [ERROR] at Unknown(0): <JProgram>
[INFO]             com.google.gwt.dev.jjs.ast.JProgram
[INFO]       [ERROR] Unrecoverable exception, shutting down
[INFO] com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
[INFO]  at com.google.gwt.dev.javac.CompilationProblemReporter.logAndTranslateException(CompilationProblemReporter.java:106)
[INFO]  at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:461)
[INFO]  at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:274)
[INFO]  at com.google.gwt.dev.CompilePerms.compile(CompilePerms.java:198)
[INFO]  at com.google.gwt.dev.ThreadedPermutationWorkerFactory$ThreadedPermutationWorker.compile(ThreadedPermutationWorkerFactory.java:50)
[INFO]  at com.google.gwt.dev.PermutationWorkerFactory$Manager$WorkerThread.run(PermutationWorkerFactory.java:74)
[INFO]  at java.base/java.lang.Thread.run(Thread.java:1583)
[INFO]       [ERROR] Not all permutation were compiled , completed (0/1)

This does not occur when you have a function that returns instead of assigning it to a variable

public class TestComposite extends Composite {
    private final TextBox textBox = new TextBox();

    public TestComposite() {
        super();
    }

    Void setText(TestEnum testEnum) {
        return switch (testEnum) {
            case A -> {
                textBox.setText("A");
                yield null;
            }
            case B -> {
                textBox.setText("B");
                yield null;
            }
        };
    }

    enum TestEnum {
        A, B
    }
}


I've tested a multitude of return types and they all lead to the same exception.

If I compile using --optimize=0, the bundle is generated without any issues.

Activity

  1. niloc132 commented on Apr 30, 2025

    @niloc132
    Member

    Thanks for the report, still working on reproducing.

    Can you confirm which GWT version you encountered this on?

    Also, while I recognize that no code patterns should have a bug like this, can you explain briefly what this code is intended to do with the Void yield?

  2. added this to the 2.13 milestone on Apr 30, 2025
  3. added a commit that references this issue on Apr 30, 2025
    f4b830d
  4. JeffreyDevloo commented on Apr 30, 2025

    @JeffreyDevloo
    Author

    The GWT version that I am using is 2.12.
    The Void is just a pattern to force a compilation error when the enum would change. All branches are just blocks of side effects but it forces me as a developer to think about implementing them when I add new entries to the enum.

    Just having

    switch (testEnum) {
                case A -> {
                    int intermediateResult = computeSomething()
                    return intermediateResult * 1
                }
    }
    

    allows for any branch to exist. No branches are enforced during compile time.

    A more common use case is to just compute a result while having intermediate steps:

            int result = switch (testEnum) {
                case A -> {
                    int intermediateResult = computeSomething()
                    yield intermediateResult * 1
                }
                case B -> {
                    int intermediateResult = computeSomething()
                    yield intermediateResult * 1
                }
            };
    

    The key here was to compile using the optimize=9 option. I did not encounter it during the codeserver compilations, only during our production build step.

  5. JeffreyDevloo commented on Apr 30, 2025

    @JeffreyDevloo
    Author

    Judging by your commit. If I had used the result, it would've worked? I've used a work around using a supplier now 😁 .

    Supplier<Void> inline = () -> switch (testEnum) {
                            case A:
                                doSomething();
                                yield null;
    ...
    
    inline.get();
    
  6. niloc132 commented on Apr 30, 2025

    @niloc132
    Member

    Thanks, I've added a few tests that try this out in a few ways, and I think the bug should be fixed in that PR.

    Yes, if you used the result, the compiler never would have removed it, and so the bug wouldn't have happened - that does seem like a valid way to work around it. Funny thing about your supplier though - in order for switch-exprs to make sense in JS (with regard to flow control), we already wrap a switch statement in a js function, so you are effectively double wrapping a switch statement in a function with your workaround. It should work though, as long as at least one callsite in your program uses the result of Supplier.get().

  7. JeffreyDevloo commented on Apr 30, 2025

    @JeffreyDevloo
    Author

    Thanks for looking into it this fast! One of the few issues I've encountered with GWT that weren't caused by me.

  8. added a commit that references this issue on Jul 23, 2025
    0396c65
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions