Fw: Constraining type annotations to occur just before type

Srikanth S Adayapalam srikanth_sankaran at in.ibm.com
Mon Apr 22 01:16:57 PDT 2013


Follow up comments from Markus Keller, IBM, Eclipse JDT/UI lead.

Srikanth

----- Forwarded by Srikanth S Adayapalam/India/IBM on 04/22/2013 01:46 PM
-----

From:	Markus Keller/Zurich/IBM at IBMCH
To:	Srikanth S Adayapalam/India/IBM at IBMIN@IBMDE,
Date:	04/22/2013 01:13 AM
Subject:	Re: Constraining type annotations to occur just before type


(Reply to Alex Buckley's mail from 4/17/2013)

// Repeating the declarations under discussion:
public @NonNull String s;
@NonNull public String s2;
@NonNull public String[] s3;
@NonNull public java.lang.String s4;
public java.lang. at NonNull String s5; // illegal before SE 8

Sorry, my "// legal ONLY for declaration/type-use annotation" comments were
due to a misconception on my part. I saw compile errors from javac
1.8.0-ea-b83 and assumed those would be correct. But in fact, with an
@NonNull with @Target({FIELD, TYPE_USE}), all these examples are
error-free. s2, s3, and s4 would only be considered as FIELD annotations,
whereas s5 would only be considered as TYPE_USE annotation.

I agree with your statements about older code and SE 5.0-era core
reflection API running on SE 8, and that it's a 100% source-compatible
change for old processors.

> 2. Where a field declaration involves an array type, the code will want
> to determine whether the @NonNull is "meant" to apply to the array type
> or to the array type's element type. If NonNull's target is just FIELD,
> it'll be the former; if NonNull's target includes TYPE_USE, it'll be the
> latter.

And that last sentence is an incompatible change. The addition of TYPE_USE
either changes the semantics of the annotation or forces annotation
processors and users of the annotation to resort to very weird hacks to
stay compatible.

The problem is this declaration:

public @NonNull String[] s6;

For the SE 8 APIs, the @NonNull annotates "String". For the SE 5.0 APIs, it
annotates the whole field s6 (but actually meant its type "String[]"). If
we stipulate that the addition of TYPE_USE should be a compatible change,
then this can only mean an SE 8 annotation processor must interpret the
@NonNull as an annotation on "String[]". That's a very bad situation. Just
because the annotation kept the FIELD target to stay compatible with old
code, that means that even new code cannot use normal SE 8 semantics. To
annotate "String", a user would have to use a trick like fully-qualifying
the type name, so that the annotation loses its SE 5.0 meaning:

public java.lang. at NonNull String[] s7;

The superficially supported combination of TYPE_USE and declaration targets
creates horrible corner cases for every user of such an annotation (even
those who only started to use them with SE 8 or later).

> I disagree strongly with this position, and here's why. Annotations are
> about three kinds of person:
>
> - the provider of an annotation type (P)
> - the applier of annotations in normal code (W)
> - the creator of code to reflect over applied annotations (R)
>
> In general, if P's migration to TYPE_USE forces removal of other
> ElementType values from @Target, there will be code from W which no
> longer compiles. (TYPE_USE is reasonably a superset of TYPE and
> TYPE_PARAMETER, but not, say, METHOD.) Since there are probably 10,000
> W's for every P, this is an unacceptable loss of source compatibility.

I would agree about this analysis if SE 5.0 annotations would actually have
been enough to create huge and valuable code bases that abuse declaration
annotations as type-use annotations. But that's not the case. That's why we
have this JSR to make type-use annotations useful in practice. I don't
think old annotations that abused declaration targets and that never worked
satisfactorily should drive SE 8 annotations. Disallowing mixed targets
avoids a lot of confusion. IDEs can help with batch-fixing code that
doesn't compile any more.

> I would rather let P migrate to TYPE_USE by adding it, which has no
> impact on any W or R person. As W's apply the annotation to type uses, a
> handful of R's will have to do some work to maintain behavioral
> compatibility. That's fine in my book.

If the W's could transparently handle the problems, then I could agree. But
unfortunately, they can't (see counterexample s6 above).


> And quite separately:
>
> It's a simple fact that a rule to constrain type annotations to occur
> just before the type tells human readers of code almost nothing. Just by
> looking at this code, what do you learn about the targets of Foo and Bar:
>
>    @Foo @Bar String s;
>
> Nothing. Foo and Bar could both target FIELD only, or both target
> TYPE_USE only, in addition to the possibility of Foo targeting FIELD
> only and Bar targeting TYPE_USE only. So the rule does not help any W
> person (of whom there are many) or anyone who reads W's code (of whom
> there are even more).

If Foo and Bar have no meaning or if they are the only modifiers, that may
be true. But in longer declarations, the language should help the human
reader and writer to keep concepts together:

   @NonNull public static final @Deprecated String s; //hard to read
   public static final @NonNull @Deprecated String s; //still strange
   @Deprecated public static final @NonNull String s; //clear

If you use an IDE that has an AST of this code, then you have various ways
to get more information. E.g. in Eclipse, you could select the annotation
or the field type and then use "Edit > Expand Selection To > Enclosing
Element (Alt+Shift+Up)". This would select enclosing elements in the AST,
so that you can immediately see whether the annotation belongs to the field
type or to the whole declaration. Or there's the Mark Occurrences feature
on method return types: Set the caret into the return type and you
immediately see the whole return type and the method's exit points marked.
Or a code formatter could put declaration annotations on separate lines but
keep type-use annotations together with the type.
All these features are only possible if the AST is regular and contains the
type-use annotations where they belong.

Markus



More information about the type-annotations-spec-experts mailing list