-
Notifications
You must be signed in to change notification settings - Fork 26
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
A way to declare a type parameter that wants nothing to do with nullness #78
Comments
I think I haven't grasped the whole picture here yet, so two questions: (1) It sounds like code owners can mostly get equivalent behavior by declaring A couple minor differences:
Based only on those minor differences, it's possible that But, a more significant possible difference:
(And maybe there are other differences?) (2) You alluded to at least one "issue" that this addresses. Can you give examples? Maybe I'm missing something obvious. |
|
I think part of my confusion is that I'm not even sure if you're saying that Beyond that: Does my previous post cover the advantages you envision for |
Let's not spend more time on this yet; I mostly just wanted to capture the fact that a type parameter whose nullness is always ignored is weird, and may bear some thinking about at some point. |
In my p.o.v. there's nothing "wrong" with Nothing of this affects the soundness of the program, it's just a matter of style, and so tools my optionally inform users that they are overzealous. I think it safe to leave this unspecified / implementation specific. IMHO, an annotation to mark this case would be overzealous itself. I do see a problem if |
My reasons for wanting to avoid
Now, one possibility is to allow people to write I should think more about the example of using |
The
(As discussed above, it's also possible to declare I still have reasons that I dislike projecting to
I stand by that. But I've also claimed:
I still feel that that's a reasonable approach to such types. But my previous claim ignored this case: Sure, my class with |
Another thing that I've been trying to get my head around is: What other fundamental differences are there between the proposed...
I'm interested in this because: If it's convenient to be able to instantiate interface BlockingQueue<E extends @Nullable Object> extends Queue<@NotNull E> {
...
@NotNull E take();
} There are some practical reasons not to do this:
I'm not sure if there's more to it than that or not. |
All methods already work in terms of `@Nullable T`, so this doesn't prevent any method from accepting null. That said, it _does_ prevent some instantiations that are safe, like `WeakReference<T>` when `<T extends @nullable Object>`. But let's try the stricter signature for now and see how much trouble it causes. This is related to jspecify/jspecify#78
I feel very good about
I'm not sure we need to keep pulling on any of the threads above. Reopen if desired. |
I still feel good about using The case is However, in order to allow users to create a I left a couple comments about this a while back in
There's also a little discussion on Google-internal bug 198793040. The main thing to note is that the use case for this is Kotlin, so we can't say "just suppress." (Now, there was also Google-internal CL 398047525, in which people were explicitly using |
I guess I'm wondering if |
It definitely would be nice for |
We've declared those classes' type parameters as having a bound of `@NonNull Object`, as discussed in jspecify/jspecify#78. This PR changes type arguments inside the JDK to be inside that bound. eisop and typetools, by contrast, use a bound of `@Nullable Object`, which they "undo" with `@NonNull T` on methods like `Class.newInstance`. Thus, they don't need or want a change like this one. (That said, they don't _universally_ use `@Nullable Object` as the bound in "cases like this," so it's possible that they'd feel that a particular API, maybe `ServiceLoader`, would be simpler with a non-nullable bound. Or, if not, they may wish to use `@NonNull S` at the use sites.) (The complicated API in this PR is `ServiceLoader`. I do see some docs that suggest that `ServiceLoader` rejects any attempt to return a `null` service instance, and that appears to be backed up by the implementation I traced through to `ProviderImpl.get`.)
We've declared those classes' type parameters as having a bound of `@NonNull Object`, as discussed in jspecify/jspecify#78. This PR changes type arguments inside the JDK to be inside that bound. eisop and typetools, by contrast, use a bound of `@Nullable Object`, which they "undo" with `@NonNull T` on methods like `Class.newInstance`. Thus, they don't need or want a change like this one. (That said, they don't _universally_ use `@Nullable Object` as the bound in "cases like this," so it's possible that they'd feel that a particular API, maybe `ServiceLoader`, would be simpler with a non-nullable bound. Or, if not, they may wish to use `@NonNull S` at the use sites.) (The complicated API in this PR is `ServiceLoader`. I do see some docs that suggest that `ServiceLoader` rejects any attempt to return a `null` service instance, and that appears to be backed up by the implementation I traced through to `ProviderImpl.get`.) (#6)
Current decision: When a generic type is equivalent whether its type parameter's argument is nullable or not (for example, Argument for changing: It would be more explicit to call out those type parameters, so that tools could either enforce that all type usages are projected, or treat plain usages as if they were projected. However, no strong case was made that such a change is worth the added complexity. Timing: This must be decided before version 1.0 of the jar. Proposal for 1.0: Finalize the current decision. If you agree, please add a thumbs-up emoji (👍) to this comment. If you disagree, please add a thumbs-down emoji (👎) to this comment and briefly explain your disagreement. Please only add a thumbs-down if you feel you can make a strong case why this decision will be materially worse for users or tool providers than an alternative. Results: Four 👍 with some requests for clarification about the need for non-null projections if the type parameter's bound is non-null (they are in fact unnecessary) and for clarification that JSpecify does not consider either a non-null bound or a nullable bound invalid in these cases. Users can choose either, although we may provide non-normative advice. The point is that there's no separate way to specify that no usages of that type parameter have parametric nullness. |
(To the last part, you shouldn't need to non-null-project a type variable if it's already non-null bounded, right?) |
To me it seems like it is up to API designers whether they want to use |
Yes, the actual design decision is only that we won't have any special solution for this. I don't think I read David's summary as implying more than that. Separately, I think we want to recommend a particular approach, but that doesn't strictly need to be decided here. |
Decision: JSpecify provides no special way to declare that none of a type parameter's usages should have parametric nullness. Users can declare the type parameter with a non-null bound and add nullable projection to some usages, or they can declare it with a nullable bound and add nullable or non-null projection to all usages. |
This may be an issue only with the current "explicit annotation on type variable usage wins" model, not with the "most-nullable" proposal.
Consider a class like this:
Note that every type usage of T has an explicit nullness override.
In this case, we really have no interest in seeing type usages of
WeakRef<@Nullable Foo>
ORWeakRef<@NotNull Foo>
at all, because the distinction between these is meaningless.Would we prefer a way to mark T itself:
Yeah, it would be hard to name this.
Then it would be an error to leave any type usage of
T
within theWeakRef
class unannotated. (Or let the defaults apply, but maybe let's not debate that yet.)The text was updated successfully, but these errors were encountered: