RFR: 8334037: Local class creation in lambda in pre-construction context crashes javac
Maurizio Cimadamore
mcimadamore at openjdk.org
Mon Jun 24 09:18:20 UTC 2024
On Sat, 22 Jun 2024 18:11:36 GMT, Vicente Romero <vromero at openjdk.org> wrote:
>> A long, long time ago, we used to have [two competing translation strategies](https://hg.openjdk.org/lambda/lambda/langtools/rev/a7e6203a332d?revcount=240) for lambda expressions. One strategy was to translate lambdas to anonymous inner classes. The other was to translate them as invokedynamic, pointing to a synthetic method containing the lambda body.
>>
>> The simpler inner class traslation strategy predates the more complex indy-based one. Since that translation strategy was based on inner classes, it made a lot of sense to do that translation _before_ `Lower`, so that any anon class generated by javac could then be translated correctly (and take into account issues such as local variable capture).
>>
>> When we added `LambdaToMethod`, it seemed natural to add it in the same step (e.g. before `Lower`) - after all, users could toggle between the two translation strategies with a switch, so it made sense to keep them close together.
>>
>> But, as we got rid of the inner class translation strategy, and doubled down on `LambdaToMethod` we have started to see some artifacts. For instance, if a lambda expression contains a local class declaration, `LambdaToMethod` has to [guess](https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java#L1480) which captured variables might get captured later in `Lower`, and re-captured them in the lambda. Now, guessing the full set of local captures is not that easy, as the set depends on the state in which `Lower` is in: more specifically, sometimes `Lower` might decide to also capture the enclosing this constructor parameter, when in early-construction context. Of course `LambdaToMethod` didn't know any of this, and over the years several [workarounds](https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java#L1244) have been introduced.
>>
>> Unfortunately, JEP 482 is in collision course with the current compiler pipeline: `Lower` is the only place in the compiler which knows how to correctly translate references to enclosing instances. Trying to mimic what `Lower` is doing in `LambdaToMethod`, doesn't scale, as the [many bugs](https://bugs.openjdk.org/issues/?jql=labels%20%3D%20javac-pre-capture) that were discovered in this area show.
>>
>> For this reason, instead of keep playing whack-a-mole, I think the time has come to finally move `LambdaToMethod` _after_ `Lower`. This move makes `LambdaToMethod` th...
>
> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java line 3893:
>
>> 3891: @Override
>> 3892: public void visitLambda(JCLambda tree) {
>> 3893: Type expectedRet = types.findDescriptorType(tree.type).getReturnType();
>
> shouldn't we erase the return type here?
`tree.type` should be erased (from `TransTypes`)
> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java line 4043:
>
>> 4041: // use a let expression so that the receiver expression is evaluated eagerly
>> 4042: return make.at(tree.pos).LetExpr(
>> 4043: make.VarDef(rcvr, translate(receiverExpression)), slam).setType(tree.type);
>
> should we erase this type?
Should be erased already, since we're past `TransTypes` and that visitor had a chance to translate the method reference expression?
> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java line 4725:
>
>> 4723: !types.isDirectSuperInterface(((JCFieldAccess)tree.selected).selected.type.tsym, currentClass);
>> 4724: tree.selected = translate(tree.selected);
>> 4725: if (tree.name == names._class && tree.selected.type.isPrimitiveOrVoid()) {
>
> not sure why this change was needed
This is again lambda deduplication. The current code will take `String.class` and rewrite it from scratch, with a new symbol in it (which is what makes deduplication fail).
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/19836#discussion_r1650656395
PR Review Comment: https://git.openjdk.org/jdk/pull/19836#discussion_r1650653767
PR Review Comment: https://git.openjdk.org/jdk/pull/19836#discussion_r1650651890
More information about the compiler-dev
mailing list