Lambdas do not copy over SYNTHETIC flag for local variables

XMLWordPrintable

    • generic
    • generic

      A DESCRIPTION OF THE PROBLEM :
      A project which I help out with recently discovered a bug with regards to lambdas; specifically, local variables within lambdas (or more specifically, their synthetic generated methods) never have the SYNTHETIC flag, even if the same source outside of a lambda would emit local variables with the SYNTHETIC flag. The bug occurs on openjdk version "25.0.1" 2025-10-21 LTS.

      Examples of source that emit SYNTHETIC-marked local variables are enhanced-for loops operating on arrays, and (what prompted our discovery) pattern-matching instanceof.

      I tested the same bug on Java 17 (17.0.14) and 21 (21.0.9), and it occurs for the pattern-matching instanceof. Interesting, testing with an array enhanced-for shows that the bug occurs only on 25, but not for 21, 17, or 8.

      This bug originates from https://git.openjdk.org/jdk/commit/cf30c203379008ebae37bf00f1839a69cd53ca26 and https://git.openjdk.org/jdk/commit/360461f13671495c07ab9881284f24191ecc3525, where only the FINAL flag is copied over for local variables when translating code for lambdas.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Compile the above class using `javac -g:vars Test.java`.
      2. Run `javap -p -c -l Test.class`.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The expected result is that both `test_noLambda` and
      `lambda$test_withLambda$0` should have the same local variable table
      with two slots: `number` and `this`.
      ACTUAL -
      The actual result is that `lambda$test_withLambda$0` has one extra local
      variable: `patt0$temp`. As the name implies, that belongs to the
      pattern-matching instanceof.

      ---------- BEGIN SOURCE ----------
      public class Test {
           private void test_noLambda() {
               if (getThing() instanceof Number number) {
                   System.out.println(number);
               }
           }
           private void test_withLambda() {
               Runnable run = () -> {
                   if (getThing() instanceof Number number) {
                       System.out.println(number);
                   }
               };
           }
           private Object getThing() {
               return null;
           }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      I have found a potential fix through the following patch (though I have not yet fully tested this):

      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      index b13c9e0fe2b..41554dae432 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      @@ -1152,7 +1152,7 @@ VarSymbol translate(final VarSymbol sym, LambdaSymbolKind skind) {
                            propagateAnnos = false;
                            break;
                        case LOCAL_VAR:
      - ret = new VarSymbol(sym.flags() & FINAL, sym.name, sym.type, translatedSym);
      + ret = new VarSymbol(sym.flags() & (SYNTHETIC | FINAL), sym.name, sym.type, translatedSym);
                            ret.pos = sym.pos;
                            // If sym.data == ElementKind.EXCEPTION_PARAMETER,
                            // set ret.data = ElementKind.EXCEPTION_PARAMETER too.

      FREQUENCY :
      ALWAYS

            Assignee:
            Jan Lahoda
            Reporter:
            Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: