<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hello,<br>
      <br>
      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.</p>
    <p>Examples of source that emit SYNTHETIC-marked local variables are
      enhanced-for loops operating on arrays, and (what prompted our
      discovery) pattern-matching instanceof.</p>
    <p>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.</p>
    <p>This bug originates from
      <a class="moz-txt-link-freetext"
href="https://git.openjdk.org/jdk/commit/cf30c203379008ebae37bf00f1839a69cd53ca26"
        moz-do-not-send="true">https://git.openjdk.org/jdk/commit/cf30c203379008ebae37bf00f1839a69cd53ca26</a>
      and
      <a class="moz-txt-link-freetext"
href="https://git.openjdk.org/jdk/commit/360461f13671495c07ab9881284f24191ecc3525"
        moz-do-not-send="true">https://git.openjdk.org/jdk/commit/360461f13671495c07ab9881284f24191ecc3525</a>,
      where only the FINAL flag is copied over for local variables when
      translating code for lambdas.<br>
      <br>
      The following source when disassembled demonstrates the problem:</p>
    <pre>/// 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</pre>
    <p>1. Compile the above class using <font face="monospace">`javac
        -g:vars Test.java`</font>.<br>
      2. Run <font face="monospace">`javap -p -c -l Test.class`</font>.<br>
      <br>
      The expected result is that both <font face="monospace">`test_noLambda`</font>
      and <font face="monospace">`lambda$test_withLambda$0`</font>
      should have the same local variable table with two slots: <font
        face="monospace">`number`</font> and `this`.<br>
      <br>
      The actual result is that <font face="monospace">`lambda$test_withLambda$0`</font>
      has one extra local variable: <font face="monospace">`patt0$temp`</font>.
      As the name implies, that belongs to the pattern-matching
      instanceof.<br>
      <br>
      I have found a potential fix through the following patch (though I
      have not yet fully tested this):</p>
    <pre>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.</pre>
    <p>I'd like to contribute the above fix myself, if possible. I've
      already submitted my OCA (pending approval). I believe my next
      step is to file a bug through <a class="moz-txt-link-freetext"
        href="https://bugs.java/" moz-do-not-send="true">https://bugs.java/</a>,
      which I'll do within a few days (or if someone tells me to).</p>
    <p>Thank you!</p>
    <p>~ Arnold Alejo Nunag, @sciwhiz12</p>
  </body>
</html>