[type-annos-observers] Fw: Constraining type annotations to occur just before type

Alex Buckley alex.buckley at oracle.com
Mon Apr 22 14:16:58 PDT 2013


On 4/22/2013 1:16 AM, Srikanth S Adayapalam wrote:
> 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 last sentence is a _source compatible_ change for all Java source 
code, but not necessarily a _behavior compatible_ change for all 
annotation processors. That's OK: source compatibility is sacrosanct, 
behavioral compatibility of compiler plugins isn't.

("Sacrosanct? Then what about 'assert'?" - well, the engineers who 
decided it was acceptable to turn an identifier into a general keyword 
have moved on. The Java language team would not make that decision today.)

> 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.

It's not ideal, but the alternative of breaking source compatibility is 
worse. All a new SE 8 annotation processor (that is, a processor which 
uses the new SE 8 reflection methods) has to do - until such time as the 
annotation type's provider makes TYPE_USE the sole target - is detect 
that NonNull is a legacy annotation type and react accordingly.

>  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.

Yes. The annotation type's provider may choose to remove FIELD from the 
annotation type's targets, but did not do so in this case. This 
flexibility is a feature, not a bug. At such time as the annotation 
type's provider makes TYPE_USE the sole target, new code will be able to 
use the new SE 8 reflection methods properly.

> 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).

No! Not for "every user" of an annotation. Be precise, please. It 
creates corner cases for the handful of people who write annotation 
processors. Everyone who applied the annotation is fine.

Look, I don't necessarily think it's _good_ to add TYPE_USE to an 
existing annotation type's targets. It obviously obscures the semantics 
of the annotation type and complicates the lives of annotation processor 
creators. But the alternatives are worse.

>> 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.

Job #0 for a language JSR is to not break existing code. Annotations 
were introduced in 2004 so there will be a decade's worth of source code 
which uses and abuses annotations by the time Java SE 8 is final. If a 
single program annotated by W prior to Java SE 8 is deemed illegal in 
the language circa Java SE 8, then this JSR will have failed.

>> 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

No, the language should not seek to organize different kinds of 
annotations. An annotation type like NonNull has semantics - 
specifically regarding inheritance, repeatability, and targetability - 
but an @NonNull annotation has no meaning in the language so the 
language should not prescribe whether it appears before or after 
'public'. The JLS can certainly _recommend_ that type annotations appear 
in one place and declaration annotations appear somewhere else, but 
enforcement is a matter for tools.

Alex


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