javac crash for annotation on lambda formal parameter type
Werner Dietl
wdietl at gmail.com
Thu Dec 5 20:48:56 PST 2013
Alex, all,
On 12/05/2013 09:49 PM, Alex Buckley wrote:
> Thanks Werner. I'm cc'ing Leonid to comment about JCK coverage of
> annotations in lambda expressions.
>
> It's wrong to lose the annotation on the header of the lambda
> expression. Instance variable initializers (and the bodies of any
> instance initializers) are necessarily compiled as if in some method,
> usually <init>, so that's where I'd expect a
> RuntimeVisibleTypeAnnotations attribute for annotations in expressions
> of instance variable initializers and instance initializers. That's what
> happens if @TA appears on the type in a 'new' expression in an instance
> variable initializer:
>
> --
> import java.lang.annotation.*;
> import java.util.function.*;
>
> @Retention(RetentionPolicy.RUNTIME)
> @Target(ElementType.TYPE_USE)
> @interface TA { };
>
> class FieldAssignment {
> IntUnaryOperator x = new @TA IntUnaryOperator() {
> public int applyAsInt(int y) { return y+1; }
> };
> }
> --
I agree that the type annotation should be present and in the remainder
of this message I discuss where they should be.
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
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.
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.
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.
In summary, we want the logic that we have for lambdas within methods to
also work for lambdas within field initializers.
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?
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.
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