NullAway icon indicating copy to clipboard operation
NullAway copied to clipboard

Nullness of generics is lost for "new Foo<@Nullable Object>().bar()"

Open agrieve opened this issue 10 months ago • 3 comments

This code:

import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

@NullMarked
public class Foo {
  private static class Inner<T extends @Nullable Object> {
    Inner<T> identity() { return this; }
  }

  Inner<@Nullable Object> mThing = new Inner<@Nullable Object>().identity();
}

Gives:

Foo.java:12: warning: [NullAway] Cannot assign from type Inner<Object> to type Inner<@Nullable Object> due to mismatched nullability of type parameters
  Inner<@Nullable Object> mThing = new Inner<@Nullable Object>().identity();
                          ^
    (see http://t.uber.com/nullaway )

If I change the assignment to two lines, it compiles without warning:

import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

@NullMarked
public class Foo {
  static class Inner<T extends @Nullable Object> {
    Inner<T> identity() { return this; }
  }

  Inner<@Nullable Object> mThing;

  Foo() {
    mThing = new Inner<@Nullable Object>();
    mThing = mThing.identity();
  }
}

agrieve avatar Mar 04 '25 16:03 agrieve

We have some workarounds for missing annotations in types of new expressions, but we don't handle this case yet. I think https://github.com/openjdk/jdk/pull/23946 might eventually solve this problem for us, assuming it lands and once it makes it into a released javac version. Otherwise, we'll have to add another workaround.

msridhar avatar Mar 16 '25 17:03 msridhar

It seems that in the latest JDK 25 early access build this bug is fixed, probably due to openjdk/jdk#23946 landing. We may hold off a bit on rolling our own fix for this, to see how this plays out and whether the JDK fix might get backported.

msridhar avatar Apr 20 '25 16:04 msridhar

Progress!

Just ran into another bug that I think is an instance of this:

@NullMarked
class Foo {
    interface Callback<T extends @Nullable Object> {
        void onResult(T result);
    }

    public void main() {
        Callback<@Nullable Integer> thing = new Callback<@Nullable Integer>() {
            @Override
            public void onResult(@Nullable Integer value) {
                Callback<@Nullable Integer> self = this;
            }
        };
    }
}
Foo.java:16: warning: [NullAway] Cannot assign from type Foo. to type Callback<@Nullable Integer> due to mismatched nullability of type parameters
                Callback<@Nullable Integer> self = this;

The wrong name I filed as #1202, but the warning is triggered because it thinks "this" is Callback<Integer> rather than Callback<@Nullable Integer> (which is why I'm guessing it's due to this javac bug)

agrieve avatar Apr 22 '25 19:04 agrieve