checker-framework icon indicating copy to clipboard operation
checker-framework copied to clipboard

`Map.merge` and other methods now always return `@Nullable`

Open cushon opened this issue 2 months ago • 0 comments

In version 3.49.3, Map.merge, Map.compute, and Map.computeIfAbsent used @PolyNull in their signatures. This gave them a non-null return type when they were used with functions that operated on non-null types. That allowed code like this:

class MapMerge {
  Integer doMerge(java.util.Map<String, Integer> m) {
    return m.merge("", 1, Integer::sum);
  }
}

When we upgrade to version 3.49.4, we see the new Map signatures from https://github.com/typetools/checker-framework/issues/7082 / https://github.com/typetools/jdk/pull/227. Those signatures use @Nullable instead of @PolyNull. That leads to a number of new errors in our codebase on code that we believe is safe. Here is a minimized example:

checker-framework-3.49.4 $ checker/bin/javac -processor nullness MapMerge.java
MapMerge.java:3: error: [return] incompatible types in return.
    return m.merge("", 1, Integer::sum);
                  ^
  type of expression: @Initialized @Nullable Integer
  method return type: @Initialized @NonNull Integer
1 error

There is currently a tradeoff between fixing #7082 and keeping the above code working.

But it may be possible for the checker to support both callers: The problem in #7082 might also be possible to fix by keeping the old Map signatures but changing the algorithm for type inference: The checker accepts the code from #7082 if we explicitly cast the MyFunction::mergeFunction method reference to the type (BiFunction<Integer, Integer, @Nullable Integer>).

(There was a previous report of type-inference problems with @PolyNull back in https://github.com/typetools/checker-framework/issues/2429. But that one looks to be fixed in recent versions, so this issue is separate.)

cushon avatar Oct 27 '25 12:10 cushon