JDK-8372635 : Lambdas do not copy over SYNTHETIC flag for local variables
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 17,25
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2025-11-25
  • Updated: 2025-12-24
  • Resolved: 2025-12-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 27
27 b03Fixed
Related Reports
Causes :  
Causes :  
Description
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
Comments
Changeset: 94c51ce3 Branch: master Author: Jan Lahoda <jlahoda@openjdk.org> Date: 2025-12-17 07:22:37 +0000 URL: https://git.openjdk.org/jdk/commit/94c51ce314eea7a4f188fa0db1bae0e3f3dbd230
17-12-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/28724 Date: 2025-12-09 15:04:42 +0000
09-12-2025

Observations on macOS 26.1: - JDK 25.0.1: Failed, LocalVariableTable for lambda$test_withLambda$0() shows patt0$temp - JDK 17.0.17: Failed, LocalVariableTable for lambda$test_withLambda$0() shows patt0$temp I -> L (Failing test that isn't affecting the overall testing) L -> H (Encountered in a common use case) W -> H (No workaround) Priority -> LHH -> P4
26-11-2025