javac crash for annotation on lambda formal parameter type

Vicente-Arturo Romero-Zaldivar vicente.romero at oracle.com
Sun Dec 8 15:36:32 PST 2013


Hi Werner,

Comments below.

On 06/12/13 23:36, Werner Dietl wrote:
> Hi Alex,
>
> thanks for confirming what the correct behaviour would be.
>
>>> Or is the lambda$new$0 method just an artifact of how javac chooses to
>>> implement Lambda?
>>> If this is an implementation detail, some of the other tests seem wrong
>>> and the type annotations in lambda expressions need a different location.
>>
>> Compilation of a lambda expression is compiler-specific, and may even vary
>> from one release of javac to another. lambda$new$0 is therefore an
>> implementation detail, but the regression tests for javac still need to be
>> aware of the detail. Please say which tests seem wrong.
> I meant to say: if we were not supposed to attach the type annotations
> on these compiler-specific methods, then the test would be wrong.
> But because attaching to these methods is correct, the tests are right
> (but confusing, as it is not obvious where the annotations are
> expected).
>
> Can anyone from the lambda-side comment on why the annotations in the
> field initializer are treated differently from the annotations in a
> method?

I have been investigating this issue, this is a corner case that we all 
skipped till now. I have been looking for related changesets and I think 
that this is not a regression.

What's the cause then?

Method Attr.visitLambda() calls a method named lambdaEnv() which returns 
an environment to be used for the attribution of the lambda expression. 
The problem here is that if the lambda expression is being used as a 
field initializer, then a fake MethodSymbol is created. This method 
symbol is not updated with any type information generated inside method 
visitLambda().

I think that there is a bug in lambda code in the sense that it should 
provide an infrastructure, a holder, for type annotations to be stored 
till the lambda method is generated. I think that depending on the 
solution type annotations code should also be modified.

Probably adding information to the now fake MethodSymbol can be a 
solution that would be transparent to current type annotation code but 
now this is just one option. Any possible solution should affect several 
lambda related areas.

I have created this bug entry to track this issue: 
https://bugs.openjdk.java.net/browse/JDK-8029787

I think this should be deferred for now, but Alex if you think that this 
should be fixed asap feel free to raise the priority to P2 and provide a 
justification.

Thanks,
Vicente

>
> cu, WMD.
>
>
> On Fri, Dec 6, 2013 at 6:11 PM, Alex Buckley <alex.buckley at oracle.com> wrote:
>> Hi Werner,
>>
>> Comments inline, but the short version is that you're on the right track.
>>
>>
>> On 12/5/2013 8:48 PM, Werner Dietl wrote:
>>> In your anonymous inner class instantiation, I agree that @TA is
>>> correctly stored in the instance initializer.
>>>
>>> If your example is modified to:
>>>
>>> class FieldAssignment {
>>>     IntUnaryOperator x = new IntUnaryOperator() {
>>>       public int applyAsInt(@TA int y) { return y+1; }
>>>     };
>>> }
>>>
>>> The annotation correctly appears on the parameter of applyAsInt in the
>>> anonymous class FieldAssignment$1:
>>>
>>>     public int applyAsInt(int);
>>>       descriptor: (I)I
>>>       flags: ACC_PUBLIC
>>>       Code:
>>>         stack=2, locals=2, args_size=2
>>>            0: iload_1
>>>            1: iconst_1
>>>            2: iadd
>>>            3: ireturn
>>>         LineNumberTable:
>>>           line 10: 0
>>>       RuntimeVisibleTypeAnnotations:
>>>         0: #15(): METHOD_FORMAL_PARAMETER, param_index=0
>>
>> Good.
>>
>>
>>> However, in the lambda example:
>>>
>>> class Lambda {
>>>     IntUnaryOperator x = (@TA int y) -> y + 1;
>>> }
>>>
>>> the compiler only creates one class file Lambda.class.
>>> It seems wrong to attach a METHOD_FORMAL_PARAMETER annotation to the
>>> instance initializer that doesn't take any parameters.
>>> Similarly, it seems wrong to add a METHOD_FORMAL_PARAMETER to the field.
>>
>> I agree.
>>
>>
>>> Looking at the "javap -v -s" output for Lambda.class I see the following
>>> method:
>>>
>>>
>>>     private static int lambda$new$0(int);
>>>       descriptor: (I)I
>>>       flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
>>>
>>>
>>> The signature of a Lambda.lambda$new$0 method is what I would expect for
>>> the lambda. So it would seem right to attach the @TA to that method.
>>
>> I agree. This was your plan in the thread "Type Annotations and Lambda" [1]
>> ... though I never found where the spec at the time
>> (java-annotation-design.pdf) mentioned annotations in lambda expressions.
>>
>> [1]
>> http://mail.openjdk.java.net/pipermail/type-annotations-dev/2013-February/000566.html
>>
>>
>>> A few test cases in
>>>
>>>
>>> langtools/test/tools/javac/annotations/typeAnnotations/referenceinfos/Lambda.java
>>>
>>> contain code like:
>>>
>>> return (@TA Object x, @TB List<@TC Object> y) -> { @TD Object l = null;
>>> System.out.println((@TE Object) l); }
>>>
>>> Only looking at the expected annotations, it seems strange how the
>>> METHOD_PARAMETER annotations are handled correctly and how this would
>>> not be ambiguous for multiple lambdas within a method.
>>>
>>> However, looking at the generated bytecode with "javap -v -p" one sees:
>>>
>>>     private static void lambda$getLambda$0(java.lang.Object,
>>> java.util.List);
>>>       descriptor: (Ljava/lang/Object;Ljava/util/List;)V
>>>       flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
>>>       Code:
>>>         stack=2, locals=3, args_size=2
>>>            0: aconst_null
>>>            1: astore_2
>>>            2: getstatic     #3                  // Field
>>> java/lang/System.out:Ljava/io/PrintStream;
>>>            5: aload_2
>>>            6: invokevirtual #4                  // Method
>>> java/io/PrintStream.println:(Ljava/lang/Object;)V
>>>            9: return
>>>         LineNumberTable:
>>>           line 22: 0
>>>         RuntimeVisibleTypeAnnotations:
>>>           0: #16(): LOCAL_VARIABLE, {start_pc=2, length=8, index=2}
>>>       RuntimeVisibleTypeAnnotations:
>>>         0: #17(): METHOD_FORMAL_PARAMETER, param_index=0
>>>         1: #18(): METHOD_FORMAL_PARAMETER, param_index=1,
>>> location=[TYPE_ARGUMENT(0)]
>>>         2: #19(): METHOD_FORMAL_PARAMETER, param_index=1
>>>
>>> So it looks like the correct bytecode is generated for this code and
>>> only the test framework makes it unclear where the annotations are
>>> actually expected.
>>
>> Yes.
>>
>>
>>> In summary, we want the logic that we have for lambdas within methods to
>>> also work for lambdas within field initializers.
>>
>> Yes.
>>
>>
>>> For the lambda within the field initializer:
>>>
>>> IntUnaryOperator x = (@TA int y) -> y + 1;
>>>
>>> we want @TA attached as METHOD_FORMAL_PARAMETER 0 on lambda$new$0.
>>>
>>> Alex, does this sound right?
>>
>> Yes.
>>
>>
>>> Or is the lambda$new$0 method just an artifact of how javac chooses to
>>> implement Lambda?
>>> If this is an implementation detail, some of the other tests seem wrong
>>> and the type annotations in lambda expressions need a different location.
>>
>> Compilation of a lambda expression is compiler-specific, and may even vary
>> from one release of javac to another. lambda$new$0 is therefore an
>> implementation detail, but the regression tests for javac still need to be
>> aware of the detail. Please say which tests seem wrong.
>>
>> FYI I will clarify in the JVMS text for RuntimeVisibleTypeAnnotations that
>> target_type 0x16 (your METHOD_FORMAL_PARAMETER) applies to a formal
>> parameter declaration of a method, constructor, or lambda expression.
>> Integrating lambda formals alongside other formals is what started me
>> looking at this in the first place.
>>
>> Thanks for your detailed explanation here.
>>
>> Alex
>>
>>
>>> cu, WMD.
>>>
>>>
>>>> Alex
>>>>
>>>> On 12/5/2013 5:51 PM, Werner Dietl wrote:
>>>>> It looks like nobody ever tested Type Annotations on Lambdas in fields.
>>>>> test/tools/javac/annotations/typeAnnotations/referenceinfos/Lambda.java
>>>>> test/tools/javac/annotations/typeAnnotations/newlocations/Lambda.java
>>>>> contain several lambda tests, but all within methods.
>>>>>
>>>>> The code that creates the NPE tries to find the owner for a parameter
>>>>> symbol.
>>>>> The expectation was that a parameter is always within an method. That
>>>>> is not true for lambdas it seems.
>>>>> Even for lambdas within a method it is not modifying the correct type:
>>>>> it modifies the enclosing method's signature instead of modifying the
>>>>> lambda's signature.
>>>>>
>>>>> As lambdas do not have an ExecutableType, the right thing is probably
>>>>> to not modify the enclosing type.
>>>>> However, I didn't see a way to determine from a parameter symbol
>>>>> whether it is a lambda parameter or a normal method parameter.
>>>>>
>>>>> Also, are the expected outputs for the referenceinfos/Lambda.java test
>>>>> correct?
>>>>> For a lambda within a field, the type annotation is currently lost.
>>>>> Where should it show up? On the field? In the instance initializer?
>>>>>
>>>>> I pushed a changeset to type-annotations with an ugly fix for the NPE
>>>>> and the expanded test cases:
>>>>>
>>>>>
>>>>> http://hg.openjdk.java.net/type-annotations/type-annotations/langtools/rev/3705299024dc
>>>>>
>>>>>
>>>>> cu, WMD.
>>>>>
>>>>>
>>>>>
>>>>> On Thu, Dec 5, 2013 at 7:47 PM, Alex Buckley <alex.buckley at oracle.com>
>>>>> wrote:
>>>>>> Starting at JDK8 b91, and continuing through b118, this innocuous
>>>>>> program
>>>>>> crashes javac:
>>>>>>
>>>>>> --
>>>>>> import java.lang.annotation.*;
>>>>>> import java.util.function.*;
>>>>>>
>>>>>> @Retention(RetentionPolicy.RUNTIME)
>>>>>> @Target(ElementType.TYPE_USE)
>>>>>> @interface TA {}
>>>>>>
>>>>>> class LambdaFormal {
>>>>>>      IntUnaryOperator x = (@TA int y) -> 1;
>>>>>> }
>>>>>> --
>>>>>>
>>>>>> with:
>>>>>>
>>>>>> java.lang.NullPointerException
>>>>>>            at
>>>>>>
>>>>>> com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.separateAnnotationsKinds(TypeAnnotations.java:302)
>>>>>>
>>>>>>            at
>>>>>>
>>>>>> com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.visitLambda(TypeAnnotations.java:1080)
>>>>>>
>>>>>>            at
>>>>>> com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1607)
>>>>>>            at
>>>>>> com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
>>>>>> ...
>>>>>>
>>>>>> This is weird because I'm sure I've annotated lambda formals in this
>>>>>> fashion
>>>>>> since b90. It's due to TYPE_USE - if TA's target is PARAMETER, then the
>>>>>> program rightly compiles, while if TA's target is FIELD, you get the
>>>>>> expected applicability error.
>>>>>>
>>>>>> Before I file a P2 bug for this, can anyone add some color to the
>>>>>> situation?
>>>>>>
>>>>>> Alex
>>>>>>
>>>>>> P.S. With a TA target of TYPE_USE, b90 produces a class file without
>>>>>> the
>>>>>> expected RuntimeVisibleTypeAnnotations attribute for @TA. But that's
>>>>>> less of
>>>>>> a concern right now.
>>>>>>
>>>>>
>>>>>
>
>



More information about the type-annotations-dev mailing list