javac crash for annotation on lambda formal parameter type
Werner Dietl
wdietl at gmail.com
Fri Dec 6 15:36:27 PST 2013
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?
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.
>>>>>
>>>>
>>>>
>>>>
>
--
http://www.google.com/profiles/wdietl
More information about the type-annotations-dev
mailing list