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