Nullness checker accepts null arguments for a possibly non-null type

3 views
Skip to first unread message

Piotr Zielinski

unread,
Nov 27, 2009, 9:46:22 AM11/27/09
to checker-fram...@googlegroups.com
Hi,

Here's another interaction between the nullness checker and generics
that baffles me

class Test {
static <F extends Object> void test1(Collection<F> collection) {
collection.add(null); } // fails, as expected
static <F extends @Nullable Object> void test2(Collection<F>
collection) { collection.add(null); } // works, why?
}

Why does nullness checker allow adding null objects to a collection
that might not accept nulls?

Thanks,
Piotr

Piotr Zielinski

unread,
Dec 3, 2009, 11:28:32 AM12/3/09
to checker-fram...@googlegroups.com
Hi,

I'm not sure how to proceed here: should I generally file these kind
of issues as bugs or should I first wait for somebody to confirm this
is not a misunderstanding on my part?

Thanks,
Piotr

Adam Warski

unread,
Dec 3, 2009, 11:34:30 AM12/3/09
to checker-fram...@googlegroups.com
I must say that generics and type parameters are quite mind-bending ;)

Just to check that I'm right:
<T extends @Nullable Object> means that T can be any type which is a subtype of @Nullable Object, for instance @NonNull String. Then null values shouldn't be allowed in the collection.

But how to accept a generic collection which allows null values then? Am I thinking right that we would need to write <T super @Nullable ?> where ? is any type (as long as it's @Nullable; that of course won't work in Java :) ). So that would make it inexpressible with generics. Which maybe isn't so surprising, as they were designed to work with java's "linear" inheritance, without supporting inheritance "diamonds" as we have with type annotations.

Also, is it true then that <@Nullable T> and <T extends @Nullable Object> are equivalent? The first one would intuitively (for me :) ) mean that the type T should be annotated with @Nullable, the second - that T is a subtype of @Nullable Object (anything), so that it may as well be annotated with @NonNull.

Adam

Mahmood Ali

unread,
Dec 3, 2009, 11:51:11 AM12/3/09
to checker-fram...@googlegroups.com
Greetings Piotr,

I want to thank you again for your continues feed of input! Please
keep it coming!

I admit that generics rules are a bit tricky to implement in many
cases. I'm pushing fixes to both of your issues (which are indeed
bugs!) soon. Sorry got busy with the holidays to fix them promptly.

As for determining whether a behavior is a bug or not; feel free to
email the mailing list or file a bug. Filing a bug would help us
manage the responses probably better, but discussing it over the
mailing lists may add value to the rest of the observers. It's your
call.

However, the rule of thumb for the checker is that if any code
violates the promise of the checker, it is a bug. For example, the
rule of Nullness Checker is that provided a properly type-checked
annotated code with no nullness suppress warnings, a null pointer
exception cannot be thrown at runtime. With `test2`, it is possible
to get a NullPointerException:

static <F extends @Nullable Object> void test2(Collection<F> col) {
col.add(null); }
...
List<@NonNull Object> list = new ArrayList<@NonNull Object>();
test2(list);
list.get(0).toString(); // throws NullPointerException

Hence it's a bug!

Sometimes this gets a bit tricky, like in the other example you provided.

Regards,
Mahmood

On Thu, Dec 3, 2009 at 11:28 AM, Piotr Zielinski

Michael Ernst

unread,
Dec 3, 2009, 1:57:14 PM12/3/09
to checker-fram...@googlegroups.com, piotr.z...@gmail.com
Piotr-

> I'm not sure how to proceed here: should I generally file these kind
> of issues as bugs or should I first wait for somebody to confirm this
> is not a misunderstanding on my part?

I'm sorry we seemed unresponsive. Soon after your message, I created a
failing test case and checked it in, and Mahmood began working to fix it.
But neither of us send a reply to the mailing list. :-( Sorry about that.

-Mike

Michael Ernst

unread,
Dec 5, 2009, 12:42:41 AM12/5/09
to checker-fram...@googlegroups.com, ad...@warski.org
Adam-

> Also, is it true then that <@Nullable T> and <T extends @Nullable Object>
> are equivalent?

They are not equivalent. (Though it has been proposed on the mailing list
that they should be.) We just realized that the meaning of <@Nullable T>
was not documented in the manual. That documentation is now in Mercurial,
and so it will be part of the next release.

> The first one would intuitively (for me :) ) mean that the type T should
> be annotated with @Nullable

Right, that is what it means. As you note, there is no other way to say
this. Thus, this is a better use for the syntax than just to shorten <T
extends @Nullable Object>. Having defaults that can shorten the latter
would still be useful.

> the second - that T is a subtype of @Nullable Object (anything), so that
> it may as well be annotated with @NonNull.

"subtype of @Nullable" and "@NonNull" mean different things. For example,
elements extracted from the first might be null, but that is not the case
for the second. The difference is even more marked if the type hierarchy
is more complex than 2 elements.

-Mike

Adam Warski

unread,
Dec 5, 2009, 8:23:38 AM12/5/09
to Michael Ernst, checker-fram...@googlegroups.com
Hello,

> Adam-
>
>> Also, is it true then that <@Nullable T> and <T extends @Nullable Object>
>> are equivalent?
>
> They are not equivalent. (Though it has been proposed on the mailing list
> that they should be.) We just realized that the meaning of <@Nullable T>
> was not documented in the manual. That documentation is now in Mercurial,
> and so it will be part of the next release.

Ah, ok. In an earlier post you and I think Artemus wrote that <@Nullable T> and <T extends @Nullable Object> are a different notation for the same thing and that only the latter is supported by the Checkers framework. Or at least so I understood :).

That would also mean that the annotations on e.g. ArrayList (checkers/jdk/nullness/src/java/util/ArrayList.java) should be: ArrayList<@Nullabel E> instead of: ArrayList<E extends @Nullable Object>.

>
>> The first one would intuitively (for me :) ) mean that the type T should
>> be annotated with @Nullable
>
> Right, that is what it means. As you note, there is no other way to say
> this. Thus, this is a better use for the syntax than just to shorten <T
> extends @Nullable Object>. Having defaults that can shorten the latter
> would still be useful.

So if <@Nullable T> is not supported by Checkers (as you wrote earlier, or is it?), is there then a way to define a collection which accepts null values?

I just checked a simple example:

public void test() {
Gen1</*@Nullable*/ String> t1 = new Gen1</*@Nullable*/ String>();
t1.add(null);

Gen2</*@Nullable*/ String> t2 = new Gen2</*@Nullable*/ String>();
t2.add(null);
}

class Gen1<E extends /*@Nullable*/ Object> {
public void add(E e) { }
}

class Gen2</*@Nullable*/ E> {
public void add(E e) { }
}

with checkers 1.0.2 and I get a typing error when constructing t2 (Gen2</*@Nullable*/ String> t2 = new Gen2</*@Nullable*/ String>()), while t1 works.

Adam

Michael Ernst

unread,
Dec 5, 2009, 4:49:32 PM12/5/09
to ad...@warski.org, checker-fram...@googlegroups.com
Adam-

A new release of the Checker Framework, with improved documentation and
implementation, is imminent. At that time, we can re-start the discussion
about whether the current semantics is desirable, and it would also be
great for you to submit bug reports against the compiler. Thanks!

-Mike
Reply all
Reply to author
Forward
0 new messages