spec clarification: type annotations on static nested types
Alex Buckley
alex.buckley at oracle.com
Mon Jan 8 20:42:46 UTC 2018
A type annotation can apply to a type that's nested (i.e. deeper) in a
*static* type though. Adapting an example from JLS 9.7.4:
---
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@interface Foo {}
class Test {
static class C {
class D {}
}
@Foo C . D x; // Legal. @Foo annotates the type which occurs in a
field declaration.
C . @Foo D y; // Legal. @Foo annotates the type of a non-static
member of C.
}
---
In the second case, the nested type which is being annotated is "C . D".
The D part denotes a non-static nested type, yes, but the first type
that we hit when stepping through the path -- C -- is static. The
storage of @Foo thus involves a type_path that descends "deeper in a
nested type" -- mentioning anything about static here would be wrong. As
a separate issue, the location where we ultimately find @Foo is, in
fact, a non-static member type, so javap helpfully shows that location
as INNER_TYPE:
Test$C$D y;
RuntimeVisibleTypeAnnotations:
0: #12(): FIELD, location=[INNER_TYPE]
(More below)
On 1/8/2018 11:39 AM, Liam Miller-Cushon wrote:
> Hello, does anyone have thoughts on whether this is a javac issue, or a
> spec and core reflection issue?
>
> Thanks,
>
> On Thu, Nov 30, 2017 at 11:04 AM, Liam Miller-Cushon <cushon at google.com
> <mailto:cushon at google.com>> wrote:
>
> JVMS 9 4.7.20.2-A says that a type_path_kind value of 1 is used to
> indicate a type annotation "is deeper in a nested type". I believe
> this should be "deeper in a *non-static* nested type". A comment in
> JDK-8027263 confirms that type path entries are used for inner
> classes, but not for static nested classes.
>
> Example:
>
> ```
> import static java.lang.annotation.ElementType.TYPE_USE;
> import static java.lang.annotation.RetentionPolicy.RUNTIME;
>
> import java.lang.annotation.Retention;
> import java.lang.annotation.Target;
> import java.lang.reflect.AnnotatedType;
> import java.util.Arrays;
>
> class T {
> @Retention(RUNTIME)
> @Target(TYPE_USE)
> @interface A {}
>
> class One {}
>
> static class Two {}
>
> void f(@A One x) {}
>
> void g(@A Two x) {}
>
> public static void main(String[] args) throws Exception {
> debug(T.class.getDeclaredMethod("f",
> One.class).getAnnotatedParameterTypes()[0]);
> debug(T.class.getDeclaredMethod("g",
> Two.class).getAnnotatedParameterTypes()[0]);
> }
>
> static void debug(AnnotatedType type) {
> System.err.println("type annotations: " +
> Arrays.toString(type.getAnnotations()));
> System.err.println(
> "owner type annotations: "
> +
> Arrays.toString(type.getAnnotatedOwnerType().getAnnotations()));
> }
> }
> ```
>
> Using type_path_kind=1 for inner classes, but not for static member
> classes, is consistent with current javac behaviour. In the example,
> the type path for the annotation on `@A One` is `[INNER_TYPE]` (One
> is an inner class), and the type path for the annotation on `@A Two`
> is empty (Two is a static member class):
>
> $ javap -v T
> ...
> void f(T$One);
> ...
> RuntimeVisibleTypeAnnotations:
> 0: #31(): METHOD_FORMAL_PARAMETER, param_index=0,
> location=[INNER_TYPE]
> ...
> void g(T$Two);
> ...
> RuntimeVisibleTypeAnnotations:
> 0: #31(): METHOD_FORMAL_PARAMETER, param_index=0
I don't like how @A has been bounced out of the nested type "T . Two" so
that it allegedly applies to the type of the formal parameter as a
whole. @A actually applies to the type of the static member Two that is
declared by T.
It's fine to annotate the type of a static member. What the
admissibility rule in JLS 9.7.4 is trying to prevent is annotating that
type from too far away:
T. at A One yy; // Legal
T. at A Two zz; // Legal
@A T.One yyy; // Legal
@A T.Two zzz; // Not legal. Because Two is static, T is deemed to be
more of a scoping construct than a type, so type annotations shan't apply.
Alex
> Using type_path_kind=1 for inner classes is not consistent with the
> current behaviour of core reflection.
> sun.reflect.annotation.AnnotatedTypeFactory adjusts type paths in
> the implementation of getAnnotatedOwnerType() for AnnotatedType and
> AnnotatedParameterizedType, and when handling ParameterizedTypes in
> nestingForType, without considering whether the nested type is
> static. This leads to apparently incorrect results for
> getAnnotations(). Note that the type annotations on `@A Two` are
> reported as being on both `Two` and its enclosing type `T`:
>
> $ javac T.java && java T.
> # `@A One`
> type annotations: [@T$A()]
> owner type annotations: []
> # `@A Two`
> type annotations: [@T$A()]
> owner type annotations: [@T$A()]
>
> My questions are:
> * if my understanding of JVMS 9 4.7.20.2-A is correct, can the spec
> be updated to clarify that type_path_kind=1 means "deeper in a
> non-static nested type", not "deeper in a nested type"?
> * is javac's output for this example correct, and if so is this a
> bug in AnnotatedTypeFactory?
>
> Thanks,
>
>
More information about the compiler-dev
mailing list