Hi Dmitry, On 11/23/21 13:45, Dmitri Gribenko wrote: >> >> Let's imagine a scenario where C3X specifies that non-qualified pointers >> are nonnull. And there's only a qualifier, _Nullable, to allow NULL. >> Asigning _Nullable to nonnull would issue a diagnostic. > > I think C3X specifying that non-qualified pointers are nonnnull would > be a showstopper, I don't think it is likely to happen given how the > users and the committee value backward compatibility that C has > offered throughout the decades.
Agreed. I even found some cases where it would cause previously-correct code to misbehave, so changing normal pointers to mean nonnull pointers is not possible at this point. Compilers may allow that with pragmas anyway. > > If I were to speculate what would happen if C3X did flip the default, > I think it would be treated by the community as a language fork. Yes > Pre-C3X headers won't work correctly when included in C3X programs, > making incremental adoption of C3X syntax, as it was intended to be > used, impossible. Projects would likely invent a NULLABLE macro, which > would expand to _Nullable in C3X and nothing in earlier versions, to > enable an incremental transition. > > That's why Clang introduced the pragma, enabling new rules to be > adopted incrementally. Let's avoid forking C :) > >> Also, do you have any experience in avoiding to diagnose a _Nullable to >> nonnull assignment _after_ explicitly comparing to NULL? I.e., allow >> the following: >> >> int *_Nullable p; >> int *q; >> >> if (!p) >> q = p; > > Internally at Google we have a checker based on the dataflow analysis > framework (https://lists.llvm.org/pipermail/cfe-dev/2021-October/069098.html) > that diagnoses usages of std::optional<T>::value() not guarded by > has_value(). We are planning to upstream it after we finish > upstreaming the dataflow framework itself. Ensuring guarded usage of > std::optional<T>::value() is very similar to diagnosing dereferences > of nullable pointers. I think a lot of the experience is transferable. > However, we haven't attempted to implement a pointer nullability check > yet, so I don't yet understand all corner cases that arise in real > world software. > > However, fundamentally, there are a few questions that you need to answer: > > - does _Nullable create a distinct type or not? It is extremely > important when you consider C++. Let's talk about _Nonnull, which is more likely to be added. I would say no. Neither const or restrict create a new type, but a qualified version of a type. _Nonnull has a behavior somewhat in between restrict and const. > > - do you want nullability-related diagnostics to be mandatory, or > optional? For example, a compiler is not required to issue a > diagnostic about a program that violates the constraints of > `restrict`. I think this should be similar to const, so a mandatory warning would be better. But still I have doubts, because this is much more complex to diagnose than const violations (there should be no diagnostic after an explicit NULL check). > > If _Nullable does not create a distinct type, and `T*` is the same > type as `T* _Nullable`, then we can't rely on the regular type system > mechanisms to issue diagnostics. > > If _Nullable creates a distinct type, then according to regular C type > checking rules, you would get a warning on the `q = p` assignment > regardless of the `if (!p)` check. That's how the C type system works, > it is not flow-sensitive. > > If we want the diagnostics to be fllow-sensitive like in your example > (I think it would be the best choice), then we need to add a new > flow-sensitive component to the C type system. I don't think there is > a precedent for this in C right now. I'm not sure how the committee or > implementors would react to such a proposal. Yes, it's complex. It should be flow-sensitive. Right now there's a precedent, I think: 'restrict'. But there are no mandatory warnings AFAIK. This would go one step further, and would probably be the most complex addition to the language ever. A minimal complying compiler would need to be much more complex than ever, which is a bad thing. Right now I have two voices in my head saying opposite things: on one hand this would add a lot of complexity to the C language, and I don't like that; on the other hand, this would make standard C much more safe than it is regarding pointers, and for the plain user, it would reduce the cognitive complexity of making sure null pointers are not passed where they shouldn't. GCC adding _Nonnull before the standard would be a good first step. Cheers, Alex