Meaning of type qualifiers designations in JSR-305

99 views
Skip to first unread message

Bill Pugh

unread,
Jul 23, 2008, 8:30:11 PM7/23/08
to jsr...@googlegroups.com
OK, to answer Mike's question, I'm going to try to describe what I
propose that JSR-305 type qualifiers mean, in a little more detail.

A type qualifier designation is described by a class (e.g.,
javax.annotation.Nonnull) and optionally a specific compile-time
constant value (e.g., the class javax.annotation.RegEx and the String
"[0-9]*]"). I'm not going to discuss (in this msg) the interaction
between two different type qualifier designations which involve the
same class but different values (e.g., @Baz(1) and @Baz(2)).

Each type qualifier designation T is associated with a set of values.
For example, the annotation @Nonnull is associated with the set of all
nonnull references, and excludes the null reference. I'll use
values(T) to denote the set of values associated with T, and values(!
T) to denote the complement of that set.

JSR-305 type designations are assumed to be immutable characteristics
of values. For example, if an reference is an element of the set of
the values associated with @Foo, then that reference is always part of
that set, and any methods invoked on the referenced object do not
change that property.

Various program elements can be denoted with both a type qualifier
designation and a when attribute. I'm not going to describe how
elements are denoted to have a type qualifier designation in this msg;
but briefly it can involve direct annotations, inherited qualifiers,
and default scoped qualifiers.

A when attribute is one of the enumeration values of
javax.annotation.meta.When:

UNKNOWN, ALWAYS, NEVER, MAYBE

Denoting a program element E with a type qualifier designation T and a
when attribute W describes a relationship between the values(T), the
set of values associated with T, and the set values(E) that be be
present at E:

w = ALWAYS: values(E) \intersection values(!T) is empty
w = NEVER: values(E) \intersection values(T) is empty
w = MAYBE: values(E) \intersection values(T) is nonempty
and values(E) \intersection values(!T) is nonempty
w = UNKNOWN true

The use of a when attribute of UNKNOWN doesn't provide any
information. However, this does allow us to treat things as though
every program element has a when attribute for every type qualifier
designation, and is also needed for overriding inherited and default
designations. ,

The intent of using type qualifier designations is to help find places
where these designations are inconsistent or will be violated. For
example, if the variable x is denoted @Foo(when=ALWAYS) and the
variable z is denoted @Foo(when=NEVER), then a static analysis tool
would likely want to generate a warning for the assignment z = x,
since z should not contain any values in values(Foo), and x is only
allowed to contain values in values(Foo).

But different tools may differ on how they use this information. For
example, some tools may wish to warn about any assignment that may
violate the relation between that program element and the values
designated by the corresponding designation and when attribute. Other
tools will try to generate warnings what they consider to be the most
blatant, likely or obvious violations, and will use their own
techniques for deciding which cases are, in fact, the most likely
violations.

Tools may also choose to report additional warnings about inconsistent
annotations. For example, if a class A has a method f(@Foo(when=MAYBE)
Object x), and class B extends A and provides an overriding definition
of f declared as f@Foo(when=NEVER) Object x), then a tool might
choose to report a violation of the Liskov substitution principle.

One issue about using annotations. At a specific method call
invocation, the set of return values from that invocation may be a
subset of the values that could be returned by an arbitrary invocation.

So, in particular, consider a method
int bar(int x)
that, in general, might return either a value that is in the set
denoted by values(Foo), or might return a value from values(!Foo).
However, at a particular method invocation site, the developer might
know that the value returned is always in values(Foo). They might know
this from the arguments passed to bar at that call site, or from the
state of the object bar is invoked on.

In short, since the annotation on the return value of a method is used
as a description of the return value at each invocation site, we might
wish to annotate the return value of bar as Foo(when=UNKNOWN) rather
than Foo(when=MAYBE).

As a concrete example, we would denote the argument to equals(Object)
as @Nonnull(when=MAYBE), since the method should always be prepared to
handle the null argument. However, we might also choose to denote the
return value of Map.get(Object) as @Nonnull(when=UNKNOWN), since for
some invocations of Map.get(..), the developer only expects nonnull
return values.

A similar issues occurs with fields. If complex state determines
whether the value stored in a field x is contained in values(Foo), we
might choose to annotate x with Foo(when=UNKNOWN) rather than
Foo(when=MAYBE).

An analysis may choose to decide it can generate more refined
information that provided by the annotations. For example, in the
following code, a static analysis tool might decide that although y is
denoted as @Foo(when=UNKNOWN), it can figure out that y contains a
value in values(Foo), and thus the assignment to z is wrong.

@Foo(when=ALWAYS) int x = 1;
@Foo(when=UNKNOWN) int y = x;
@Foo(when=NEVER) int z = y;

Radu Grigore

unread,
Jul 24, 2008, 12:01:20 PM7/24/08
to jsr...@googlegroups.com
It sounds like this definition is good enough to say when a tool must
not give a warning. In all other situations a tool is allowed to give
a warning. Is this correct?

On Thu, Jul 24, 2008 at 1:30 AM, Bill Pugh <pu...@cs.umd.edu> wrote:
> One issue about using annotations. At a specific method call
> invocation, the set of return values from that invocation may be a
> subset of the values that could be returned by an arbitrary invocation.
>
> So, in particular, consider a method
> int bar(int x)
> that, in general, might return either a value that is in the set
> denoted by values(Foo), or might return a value from values(!Foo).
> However, at a particular method invocation site, the developer might
> know that the value returned is always in values(Foo).

Perhaps the developer should have a way of encoding this information?
Something like a 'cast' at the call site. That could be used by tools.

--
regards,
radu
http://rgrig.blogspot.com/

Reply all
Reply to author
Forward
0 new messages