javac crash for annotation on lambda formal parameter type
Alex Buckley
alex.buckley at oracle.com
Fri Dec 6 15:11:09 PST 2013
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