Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullness checker handling of implicit type variable bounds - difference between <T> and <T extends @Nullable Object> #5075

Open
cushon opened this issue Mar 7, 2022 · 2 comments
Assignees

Comments

@cushon
Copy link
Contributor

cushon commented Mar 7, 2022

In the following example, the nullness checker rejects an input that contains a type variable with an explicit extends @Nullable Object bound, and accepts the same example if the explicit bound is removed.

The CLIMB-to-top defaults say implicit type variable bounds are interpreted as equivalent extends @Nullable Object, is this a bug?

import org.checkerframework.checker.nullness.qual.Nullable;

class A<V extends @Nullable Object> {
  I<V> i() {
    return new B<>(this);
  }
}

class B<K, V> implements I<V> {
  B(A<V> a) {}
}

interface I<V> {}

With Checker Framework 3.21.3:

$ ./checker-framework-3.21.3/checker/bin/javac -processor nullness A.java
A.java:5: error: [return] incompatible types in return.
    return new B<>(this);
           ^
  type of expression: @Initialized @NonNull B<@Initialized @NonNull Object, V extends @Initialized @NonNull Object>
  method return type: @Initialized @NonNull I<V extends @Initialized @Nullable Object>
A.java:5: error: [argument] incompatible argument for parameter a of B.
    return new B<>(this);
                   ^
  found   : @Initialized @NonNull A<V extends @Initialized @Nullable Object>
  required: @Initialized @NonNull A<V extends @Initialized @NonNull Object>
2 errors

The code compiles without error if extends @Nullable Object is removed.

diff A.java B.java
3c3
< class A<V extends @Nullable Object> {
---
> class A<V> {
$ ./checker-framework-3.21.3/checker/bin/javac -processor nullness B.java
@smillst
Copy link
Member

smillst commented Mar 7, 2022

Yes, this a bug. For some reason, the code that infers the type arguments for the diamond is ignoring the explicit annotation on the type variable. (The errors disappear with new B<Object, V>(this);)

@cushon
Copy link
Contributor Author

cushon commented Mar 7, 2022

Thanks @smillst for taking a look!

Here's one more example that seems closely related, where adding an explicit type argument doesn't seem to help:

import org.checkerframework.checker.nullness.qual.Nullable;

class C<V extends @Nullable Object> {
  I<V> c(N n) {
    return h(n.i());
  }

  abstract class N {
    abstract I<V> i();
  }

  static <V> I<V> h(I<V> i) {
    return i;
  }
}

interface I<V> {}
./checker-framework-3.21.3/checker/bin/javac -processor nullness C.java
C.java:5: error: [return] incompatible types in return.
    return h(n.i());
            ^
  type of expression: @Initialized @NonNull I<V extends @Initialized @NonNull Object>
  method return type: @Initialized @NonNull I<V extends @Initialized @Nullable Object>
1 error

The same diagnostic is reported after adding an explicit type argument:

diff C.java D.java
5c5
<     return h(n.i());
---
>     return C.<V>h(n.i());
./checker-framework-3.21.3/checker/bin/javac -processor nullness D.java
D.java:5: error: [argument] incompatible argument for parameter i of h.
    return C.<V>h(n.i());
                     ^
  found   : @Initialized @NonNull I<V extends @Initialized @NonNull Object>
  required: @Initialized @NonNull I<V extends @Initialized @Nullable Object>
1 error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants