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

Introduce a NullSpecTypeValidator to ensure unspecified type variables are ok #178

Open
wants to merge 3 commits into
base: main-eisop
Choose a base branch
from

Conversation

wmdietl
Copy link
Collaborator

@wmdietl wmdietl commented Apr 23, 2024

Fixes #177.

Depends on eisop/checker-framework#746 to also correctly default type variable lower bounds.
@cpovirk If it is easy, could you see whether that EISOP PR introduces any new issues?

With both of these changes, the conformance tests should pass again.

@wmdietl wmdietl requested a review from cpovirk April 23, 2024 01:11
@wmdietl wmdietl linked an issue Apr 23, 2024 that may be closed by this pull request
@cpovirk
Copy link
Collaborator

cpovirk commented Apr 23, 2024

The code change looks reasonable. Assuming that I did the testing with eisop/checker-framework#746 correctly, however, I'm now seeing at least a few dozen new errors.

It's mostly errors like this:

com/google/common/collect/Streams.java:375: error: [override.param.invalid] Incompatible parameter type for action.
              public boolean tryAdvance(Consumer<? super R> action) {
                                                            ^
  found   : Consumer<? super R>
  required: Consumer<? super R*>
  Consequence: method in 
    boolean tryAdvance(Consumer<? super R>)
  cannot override method in Spliterator<R*>
    boolean tryAdvance(Consumer<? super R*>)

And some of this similar kind of error:

com/google/common/collect/TreeMultimap.java:222: error: [assignment.type.incompatible] incompatible types in assignment.
    keyComparator = requireNonNull((Comparator<? super K>) stream.readObject());
                                  ^
  found   : Comparator<capture#845 of ? super K>
  required: Comparator<? super K>

I also see a tiny bit of this:

com/google/common/collect/Multisets.java:1137: error: [argument.type.incompatible] incompatible argument for parameter o of Collections.nCopies.
        entry -> Collections.nCopies(entry.getCount(), entry.getElement()).spliterator(),
                                                                       ^
  found   : E*
  required: E

(As usual, the line numbers there might not exactly line up with the public version.)

One small note: It looks like I had been testing past PRs with the most recent version of eisop/checker-framework that we'd imported already. That version doesn't include any changes from April. That might not matter, but I point it out to say that it's at least possible that any problems "with this PR" actually come from some recent eisop/checker-framework change that I only now imported. However, I'm not sure what that would be other than eisop/checker-framework@1d2bd6c. And when I make that change in my previous test client (which had a combination of #171 and #175), I don't see any errors. So I think that the errors really do result from this PR or the eisop/checker-framework PR.

@wmdietl
Copy link
Collaborator Author

wmdietl commented Apr 30, 2024

@cpovirk Thanks for testing!

I've merged eisop/checker-framework#746 now.
Can you please let me know whether the errors in Streams.java and TreeMultimap.java are gone? I do hope so :-)

Locally, the conformance tests for this PR pass for me.

Adding the NullSpecTypeValidator only makes a difference of 4 test cases, and only in strict mode. Things are better with the more lenient validator.

On the highest level, for the jspecifySamplesTest, I now see:

tests.NullSpecTest$Lenient > run[/home/wdietl/tmp/jspecify/jspecify-reference-checker/build/conformanceTests/samples] FAILED
    java.lang.AssertionError: 1200 out of 1217 expected diagnostics were found.
    18 unexpected diagnostics were found:
....
    17 expected diagnostics were not found:
....
tests.NullSpecTest$Lenient > run[/home/wdietl/tmp/jspecify/jspecify-reference-checker/build/conformanceTests/samples/nullnessUnspecifiedTypeParameter/nullnessunspecifiedtypeparameter] FAILED
    java.lang.AssertionError: 7 out of 7 expected diagnostics were found.
    1 unexpected diagnostic was found:
....
tests.NullSpecTest$Strict > run[/home/wdietl/tmp/jspecify/jspecify-reference-checker/build/conformanceTests/samples] FAILED
    java.lang.AssertionError: 1211 out of 1217 expected diagnostics were found.
    180 unexpected diagnostics were found:
....
    6 expected diagnostics were not found:
....
tests.NullSpecTest$Strict > run[/home/wdietl/tmp/jspecify/jspecify-reference-checker/build/conformanceTests/samples/annotatedBoundsOfWildcard/annotatedboundsofwildcard] FAILED
    java.lang.AssertionError: 21 out of 21 expected diagnostics were found.
    7 unexpected diagnostics were found:
....
tests.NullSpecTest$Strict > run[/home/wdietl/tmp/jspecify/jspecify-reference-checker/build/conformanceTests/samples/wildcardsWithDefault/wildcardswithdefault] FAILED
    java.lang.AssertionError: 2 out of 2 expected diagnostics were found.
    2 unexpected diagnostics were found:
....
34 tests completed, 6 failed

The biggest blocker are the 180 unexpected diagnostics in strict mode. Do you see a pattern do those?

Picking one of the lenient failures:

jspecify-reference-checker/build/conformanceTests/samples/MultiBoundTypeVariableUnspecToObject.java:55:Error: (return.type.incompatible) incompatible types in return.
        return x;
               ^
      type of expression: T*
      method return type: Object

In lenient mode, should T* be a subtype of Object? Currently, it is not, even in lenient mode. Is that a problem with the hierarchy or the test case? I don't see a reason why the bounds of T should matter, as there is an explicit "unspecified" annotation on the use of T.

Another question:

tests.NullSpecTest$Lenient > run[/home/wdietl/tmp/jspecify/jspecify-reference-checker/build/conformanceTests/samples/nullnessUnspecifiedTypeParameter/nullnessunspecifiedtypeparameter] FAILED
    java.lang.AssertionError: 7 out of 7 expected diagnostics were found.
    1 unexpected diagnostic was found:
      /home/wdietl/tmp/jspecify/jspecify-reference-checker/build/conformanceTests/samples/nullnessUnspecifiedTypeParameter/nullnessunspecifiedtypeparameter/NullnessUnspecifiedTypeParameter.java:34:Error: (type.argument.type.incompatible) incompatible type argument for type parameter T of NullnessUnspecifiedTypeParameter.
      static final NullnessUnspecifiedTypeParameter<@Nullable Object> A2 =
                                                    ^
      found   : Object?
      required: Object

Looking at conformanceTests/samples/nullnessUnspecifiedTypeParameter/nullnessunspecifiedtypeparameter/NullnessUnspecifiedTypeParameter.java I do not understand the name of class NullnessUnspecifiedTypeParameter:

@NullMarked
public class NullnessUnspecifiedTypeParameter<T> {

Should the @NullMarked be @NullUnmarked?
As it is now, the error makes sense to me.
If it should be @NullUnmarked, it depends on whether in lenient mode it should be an error to pass @Nullable to a type parameter that is unspecified - i.e. it could be changed to non-null-bounded later.

Sorry for the long message. I made one long comment, but it might be easier to split up replies into individual comments. Or whatever works best for you :-)

@cpovirk
Copy link
Collaborator

cpovirk commented May 1, 2024

Assuming that I did the testing right, I'm still seeing the errors from last time even after importing eisop/checker-framework#746 :(

More on the rest in a bit.

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

Successfully merging this pull request may close these issues.

Unmarked type variables bounds in strict mode
2 participants