From alex.buckley at oracle.com Tue Apr 1 00:45:40 2014 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 31 Mar 2014 17:45:40 -0700 Subject: ordering of annotated array brackets In-Reply-To: <5314DD50.4070100@oracle.com> References: <5314DD50.4070100@oracle.com> Message-ID: <533A0C34.4030505@oracle.com> Per the thread on the observer list [1], I have filed https://bugs.openjdk.java.net/browse/JDK-8038881 to modify the JLS. It is instructive to compare the informal text in the old JSR 308 spec (commented out in September 2011) with the actual JLS text needed to effect the desired language design. Alex [1] http://mail.openjdk.java.net/pipermail/type-annotations-spec-observers/2014-April/000289.html On 3/3/2014 11:51 AM, Alex Buckley wrote: > Hi Markus, > > Thanks for your mail and excellent test case. > > However, I don't see why the post-identifier "@C []" should be the > outermost dimension, as if "int @C [] @B []" had been written. Certainly > there was no hint of this in the January 2013 thread "Annotations on > extended dimension in method declarations". > > I think the "followed by ..." wording of 10.2 indicates that "@C []" > should be the innermost (deepest) dimension, as if "int @B [] @C []" had > been written. > > I am not clear if javac or java.lang.reflect is assembling the full > array type in (what I consider to be) the wrong order. Werner, if you're > reading this, can you help us out? > > Alex > > On 3/3/2014 8:29 AM, Markus Keller wrote: >> JLS8 10.2 says: >> "In a variable declaration (?8.3, ?8.4.1, ?9.3, ?14.14, ?14.20) except >> for >> a variable >> arity parameter, the array type of a variable is denoted by the array >> type >> that appears >> at the beginning of the declaration, followed by any bracket pairs that >> follow the >> variable's Identifier in the declarator." >> >> This was fine for Java < 8, where array dimension brackets were >> indistinguishable. But with type annotations, the order of annotated >> dimension brackets matters. >> >> Example: private @A int @B [] field @C []; >> >> In "1.8.0-b129", reflection says the type of "field" is "@A int @C [] @B >> []", and that totally makes sense. See also [1]. >> >> The paragraph should e.g. say: >> "[..] the array type of a variable is denoted by the element type at the >> beginning of the declaration, followed by any (potentially annotated) >> bracket pairs that follow the variable's Identifier in the declarator, >> followed by any (potentially annotated) bracket pairs of the array >> type at >> the beginning of the declaration." >> >> The ordering of consecutive array dimension brackets is currently also >> undefined by the spec. There's just an informal example in 9.7.4 >> (which is >> good; it's just not a spec). >> >> Regards, >> Markus >> >> >> [1] Executable snippet to test reflection: >> >> import java.lang.annotation.*; >> import java.lang.reflect.*; >> import java.util.stream.*; >> >> @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) >> @interface A {} >> @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) >> @interface B {} >> @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) >> @interface C {} >> >> public class Snippet { >> private @A int @B [] field @C []; >> private @A int @C [] @B [] fieldNormal; >> private @A int @B [] fieldBase, fieldExtra @C []; >> >> public static void main(String[] args) throws Exception { >> printField("field"); >> printField("fieldNormal"); >> printField("fieldBase"); >> printField("fieldExtra"); >> } >> >> private static void printField(String name) throws >> NoSuchFieldException { >> System.out.printf("%-12s %s\n", name + ":", >> toString(Snippet.class.getDeclaredField(name).getAnnotatedType())); >> } >> >> private static String toString(AnnotatedType annotatedType) { >> StringBuilder sb = new StringBuilder(); >> while (annotatedType instanceof AnnotatedArrayType) { >> sb.append(" >> ").append(toString(annotatedType.getAnnotations())).append("[]"); >> annotatedType = ((AnnotatedArrayType) >> annotatedType).getAnnotatedGenericComponentType(); >> } >> Type type = annotatedType.getType(); >> sb.insert(0, type.toString()); >> sb.insert(0, toString(annotatedType.getAnnotations())); >> return sb.toString(); >> } >> >> private static String toString(Annotation[] annotations) { >> return Stream.of(annotations).map(Snippet::toSimpleString). >> collect(Collectors.joining(" ", "", " ")); >> } >> >> public static String toSimpleString(Annotation a) { >> return "@" + a.annotationType().getSimpleName(); >> } >> } >> From alex.buckley at oracle.com Fri Apr 4 19:04:39 2014 From: alex.buckley at oracle.com (Alex Buckley) Date: Fri, 04 Apr 2014 12:04:39 -0700 Subject: annotated type of array-typed varargs parameter In-Reply-To: References: Message-ID: <533F0247.3050006@oracle.com> Hi Markus, I think it's pretty clear that the "intuitive order" is desirable, for two reasons: 1) It should be possible to refactor pArr's type to match pVar's type by replacing the final [] with '...', and not change the array type of pVar. That is, the following method call (in someone else's code, so can't be easily changed) should keep working after the refactoring: var(new Object @A [] {}); 2) I see this more as a spec issue than a true design issue. Formerly, the array type was specified in recursive fashion, building on the component type ("the component type is the UnannType"). That's why, for a variable arity parameter, it looked like the compiler was constructing an array of type Object @B [] @A []. But we've generally come to specify array types in a linear fashion, which would mean the compiler is deemed to construct an array of type Object @A [] @B [] (or Object @A [] if you omit later dimensions, as in method call above). With reference to JDK-8038881, I'd propose to have 8.4.1 always point to 10.2 for a variable arity parameter, and for 10.2 to build up the parameter's type in basically the same fashion as for other array types [excluding post-identifier [], of course, since they're not legal here]. Happily, JLS 15.12.2 which considers variable arity parameters speaks only of the full array type T[] which is the type of the last parameter. So that is unaffected by clarifications to 8.4.1 / 10.2, as long as 8.4.1 / 10.2 are deterministic about the outermost dimension. Alex On 4/4/2014 7:14 AM, Markus Keller wrote: > We've found a problem with the definition of the varargs syntax on array > types. The methods "var" and "arr" below should intuitively have the same > formal parameter type "Object @A [] @B []", but that clashes with the > order of annotations on array dimensions: > > interface Test { > void var(Object @A [] @B ... pVar); > void arr(Object @A [] @B [] pArr); > } > @Target(ElementType.TYPE_USE) @interface A { } > @Target(ElementType.TYPE_USE) @interface B { } > > The clarification for 8.4.1 in > https://bugs.openjdk.java.net/browse/JDK-8038881 says: "If the formal > parameter is a variable arity parameter, then the declared type is an > array type whose component type is UnannType." > > In "var", the UnannType is "Object @A []", so the type of the parameter is > actually "Object @B [] @A []", according to the annotation order given in > 9.7.4. > > In JDK 1.8.0-b132, reflection uses the intuitive order. The snippet below > prints: > > var: class java.lang.Object @A [] @B [] > arr: class java.lang.Object @A [] @B [] > > That's incorrect according to JLS8. java-annotation-design.html doesn't > address this problem (just says "The varargs syntax ?...? is treated > analogously to array brackets"). > > I don't see a good solution. > a) Say JLS8 is right. => We lose the simple transformation that turns a > vararg into a normal array dimension by changing ... into []. > b) Say the above transformation must be valid. => The annotation in front > of the ... no longer applies to the outermost array type, which is also > strange, because that's the array that is constructed by the compiler for > a variable arity method invocation. > > > > import java.lang.annotation.*; > import java.lang.reflect.*; > import java.util.stream.*; > > @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) > @interface A {} > @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) > @interface B {} > > public class Snippet { > void var(Object @A [] @B ... pVar) { } > void arr(Object @A [] @B [] pArr) { } > > public static void main(String[] args) throws Exception { > printParamType(Snippet.class.getDeclaredMethod("var", > Object[][].class)); > printParamType(Snippet.class.getDeclaredMethod("arr", > Object[][].class)); > } > > private static void printParamType(Method method) { > System.out.printf("%-5s %s\n", method.getName() + ":", > toString(method.getAnnotatedParameterTypes()[0])); > } > > private static String toString(AnnotatedType annotatedType) { > StringBuilder sb = new StringBuilder(); > while (annotatedType instanceof AnnotatedArrayType) { > sb.append(" ") > .append(toString(annotatedType.getAnnotations())) > .append("[]"); > annotatedType = ((AnnotatedArrayType) annotatedType) > .getAnnotatedGenericComponentType(); > } > Type type = annotatedType.getType(); > sb.insert(0, type.toString()); > sb.insert(0, toString(annotatedType.getAnnotations())); > return sb.toString(); > } > > private static String toString(Annotation[] annotations) { > return Stream.of(annotations).map(Snippet::toSimpleString). > collect(Collectors.joining(" ", "", " ")); > } > > public static String toSimpleString(Annotation a) { > return "@" + a.annotationType().getSimpleName(); > } > } > > Markus > From alex.buckley at oracle.com Thu Apr 10 21:43:45 2014 From: alex.buckley at oracle.com (Alex Buckley) Date: Thu, 10 Apr 2014 14:43:45 -0700 Subject: [type-annos-observers] annotated type of array-typed varargs parameter In-Reply-To: References: <533F0247.3050006@oracle.com> Message-ID: <53471091.5010905@oracle.com> Thanks Markus, I updated JDK-8038881 for 8.4.1 and 10.2. There's no need for "(possibly annotated)" as generally the JLS doesn't flag where annotations are possible - that's centralized in 9.7.4. The SE API spec has a similar trick: java.lang.reflect.AnnotatedType speaks of a "potentially annotated use of a type" so that we don't have to say that in all the places which return AnnotatedType, e.g. Class#getAnnotatedSuperclass, Field#getAnnotatedType, etc. Alex On 4/6/2014 12:43 PM, Markus Keller wrote: > Hi Alex > > Thanks for the clarification. > > 8.4.1 also defines: "a variable arity parameter, indicated by an ellipsis > following the type." Strictly speaking, it should be "indicated by a > (possibly annotated) ellipsis following the type". > > Using 10.2 to hold the definition of a variable's type sounds good. The > final text should also tell that annotations belong to the bracket pair or > ellipsis that follows them, so that it's clear that the annotations are > reason for the explicit specification of the order of the array > dimensions. > > Markus > > "type-annotations-spec-observers" > wrote on > 2014-04-04 21:04:39: > >> From: Alex Buckley >> To: type-annotations-spec-experts > >> Date: 2014-04-04 21:04 >> Subject: Re: [type-annos-observers] annotated type of array-typed > varargs parameter >> Sent by: "type-annotations-spec-observers" > >> >> Hi Markus, >> >> I think it's pretty clear that the "intuitive order" is desirable, for >> two reasons: >> >> 1) It should be possible to refactor pArr's type to match pVar's type by > >> replacing the final [] with '...', and not change the array type of >> pVar. That is, the following method call (in someone else's code, so >> can't be easily changed) should keep working after the refactoring: >> >> var(new Object @A [] {}); >> >> 2) I see this more as a spec issue than a true design issue. Formerly, >> the array type was specified in recursive fashion, building on the >> component type ("the component type is the UnannType"). That's why, for >> a variable arity parameter, it looked like the compiler was constructing > >> an array of type Object @B [] @A []. But we've generally come to specify > >> array types in a linear fashion, which would mean the compiler is deemed > >> to construct an array of type Object @A [] @B [] (or Object @A [] if you > >> omit later dimensions, as in method call above). >> >> With reference to JDK-8038881, I'd propose to have 8.4.1 always point to > >> 10.2 for a variable arity parameter, and for 10.2 to build up the >> parameter's type in basically the same fashion as for other array types >> [excluding post-identifier [], of course, since they're not legal here]. >> >> Happily, JLS 15.12.2 which considers variable arity parameters speaks >> only of the full array type T[] which is the type of the last parameter. > >> So that is unaffected by clarifications to 8.4.1 / 10.2, as long as >> 8.4.1 / 10.2 are deterministic about the outermost dimension. >> >> Alex >> >> On 4/4/2014 7:14 AM, Markus Keller wrote: >>> We've found a problem with the definition of the varargs syntax on > array >>> types. The methods "var" and "arr" below should intuitively have the > same >>> formal parameter type "Object @A [] @B []", but that clashes with the >>> order of annotations on array dimensions: >>> >>> interface Test { >>> void var(Object @A [] @B ... pVar); >>> void arr(Object @A [] @B [] pArr); >>> } >>> @Target(ElementType.TYPE_USE) @interface A { } >>> @Target(ElementType.TYPE_USE) @interface B { } >>> >>> The clarification for 8.4.1 in >>> https://bugs.openjdk.java.net/browse/JDK-8038881 says: "If the formal >>> parameter is a variable arity parameter, then the declared type is an >>> array type whose component type is UnannType." >>> >>> In "var", the UnannType is "Object @A []", so the type of the > parameter is >>> actually "Object @B [] @A []", according to the annotation order given > in >>> 9.7.4. >>> >>> In JDK 1.8.0-b132, reflection uses the intuitive order. The snippet > below >>> prints: >>> >>> var: class java.lang.Object @A [] @B [] >>> arr: class java.lang.Object @A [] @B [] >>> >>> That's incorrect according to JLS8. java-annotation-design.html > doesn't >>> address this problem (just says "The varargs syntax ?...? is treated >>> analogously to array brackets"). >>> >>> I don't see a good solution. >>> a) Say JLS8 is right. => We lose the simple transformation that turns > a >>> vararg into a normal array dimension by changing ... into []. >>> b) Say the above transformation must be valid. => The annotation in > front >>> of the ... no longer applies to the outermost array type, which is > also >>> strange, because that's the array that is constructed by the compiler > for >>> a variable arity method invocation. >>> >>> >>> >>> import java.lang.annotation.*; >>> import java.lang.reflect.*; >>> import java.util.stream.*; >>> >>> @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) >>> @interface A {} >>> @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) >>> @interface B {} >>> >>> public class Snippet { >>> void var(Object @A [] @B ... pVar) { } >>> void arr(Object @A [] @B [] pArr) { } >>> >>> public static void main(String[] args) throws Exception { >>> printParamType(Snippet.class.getDeclaredMethod("var", >>> Object[][].class)); >>> printParamType(Snippet.class.getDeclaredMethod("arr", >>> Object[][].class)); >>> } >>> >>> private static void printParamType(Method method) { >>> System.out.printf("%-5s %s\n", method.getName() + ":", >>> toString(method.getAnnotatedParameterTypes()[0])); >>> } >>> >>> private static String toString(AnnotatedType annotatedType) { >>> StringBuilder sb = new StringBuilder(); >>> while (annotatedType instanceof AnnotatedArrayType) { >>> sb.append(" ") >>> .append(toString(annotatedType.getAnnotations())) >>> .append("[]"); >>> annotatedType = ((AnnotatedArrayType) annotatedType) >>> .getAnnotatedGenericComponentType(); >>> } >>> Type type = annotatedType.getType(); >>> sb.insert(0, type.toString()); >>> sb.insert(0, toString(annotatedType.getAnnotations())); >>> return sb.toString(); >>> } >>> >>> private static String toString(Annotation[] annotations) { >>> return Stream.of(annotations).map(Snippet::toSimpleString). >>> collect(Collectors.joining(" ", "", " ")); >>> } >>> >>> public static String toSimpleString(Annotation a) { >>> return "@" + a.annotationType().getSimpleName(); >>> } >>> } >>> >>> Markus >>> >> >