[cfe-dev] ISO C3X proposal: nonnull qualifier
Alejandro Colomar (man-pages) via cfe-dev
cfe-dev at lists.llvm.org
Tue Nov 16 04:34:35 PST 2021
Hi Joseph,
On 11/15/21 23:47, Joseph Myers wrote:
> On Mon, 15 Nov 2021, Alejandro Colomar (man-pages) via Gcc wrote:
>
>> Hi Joseph,
>>
>> On 11/15/21 23:17, Joseph Myers wrote:
>>> On Mon, 15 Nov 2021, Alejandro Colomar (man-pages) via Gcc wrote:
>>>
>>>> How is restrict handling that problem of lvalue-to-rvalue already?
>>>
>>> restrict has tricky rules about "based on" (6.7.3.1).
>>
>> Hmm, I think I can "base on" that,
>> to define what I had in mind. :)
>
> "based on" is about optimizations; I think it's even less suited to
> anything relating to diagnostics than it is to optimization.
>
> To restrict assignment between different kinds of pointers, I'd think
> you'd want pointer type variants that differ in some way *other* than
> qualifiers, a way that's unaffected by lvalue-to-rvalue conversion, but
> that comes with its own rules on implicit conversion as if by assignment
> (6.5.16.1) (though then you also need to work out what's allowed in terms
> of mixing these pointer type variants in all the other operations allowing
> pointers, what type results of pointer arithmetic have, etc.). And there
> should surely also be some way of converting a normal pointer to this
> variant with a runtime check for NULL.
>
> Note that discussion of prior art in such a proposal should also consider
> relevant prior art (for constraining possible values of a variable through
> the type system) in C++ or other languages if possible.
>
The only other language that I know is C++, so I'll talk about it:
C++ added long ago something close to this: references.
However, IMO:
- They don't provide any enforced safety at all.
It's just as safe as using [[gnu::nonnull]].
See an example code below showing why.
- They unnecessarily changed pointer syntax to use value syntax.
This syntax has not been widely accepted by some C programmers,
which would have to adapt their minds considerably.
Clang's _Nonnull (and _Nullable) qualifiers (are they?):
- Do have the safety that C++ references should have had,
if they had been properly designed.
Again, see the example code below.
- They have an intuitive syntax for C programmers,
by just adding a qualifier to a pointer,
you mark it as either possibly being NULL or not.
It has the appearance of a qualifier,
and the documentation refers to it as a qualifier,
but how does it implement it clang internally?
Is it implemented as a completely different type
with some implicit conversion rules?
I don't know.
See
<https://clang.llvm.org/docs/AttributeReference.html#nullability-attributes>.
---
$ cat nonnull.c
[[gnu::returns_nonnull]]
int *foo(int *p)
{
return p;
}
/*
* No (nonnull-related) diagnostics from the following commands:
* $ gcc -Wall -Wextra -std=gnu2x -S nonnull.c
* $ clang -Weverything -std=gnu2x -S nonnull.c
*/
/*++++++++++++*/
/* Clang only */
int *_Nonnull bar(int *_Nullable p)
{
return p;
}
/*
* Clang provides safety here:
*
* $ clang -Weverything -std=gnu2x -S nonnull.c
* nonnull.c:17:9: warning: implicit conversion from nullable pointer
'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'
[-Wnullable-to-nonnull-conversion]
* return p;
* ^
*/
/*++++++++++++*/
/* C++ only */
int *baz(int *p)
{
int &q = *p;
return &q;
}
/* No (nonnull-related) diagnostics from the following commands:
* $ gcc -Wall -Wextra -std=gnu++2b -S nonnull.cxx
* $ clang -Weverything -std=gnu++20 -S nonnull.cxx
*/
---
So we have the following list of prior art:
- [[gnu::nonnull]] (GCC)
- _Nonnull (Clang)
- & (references) (C++)
From which I completely dislike references,
for not adding any real benefits,
and being unnecessarily unintuitive (to C programmers).
I like from _Nonnull that it enforces diagnostics.
And I like from [[gnu::nonnull]] that it allows optimizations.
I'm trying to mix them in a good way.
Since Clang's _Nonnull is closer to what I have in mind,
I did further tests to _Nonnull,
to see what I like,
and what I dislike:
---
$ cat _Nonnull.c
#include <stdlib.h>
int *_Nonnull f(int *_Nullable p)
{
if (!p)
exit(1);
return p;
}
int *_Nonnull g(int *_Null_unspecified p)
{
return p;
}
int *_Nonnull h(int *p)
{
return p;
}
int *_Nullable i(int *_Nonnull p)
{
return p;
}
---
First of all,
I see unnecessary (probably over-engineered) qualifiers:
- _Null_unspecified seems to me the same as nothing.
If I didn't specify its nullability,
it's by definition unspecified. Right?
- _Nullable seems to me also the same as nothing.
The language allows for a pointer to be NULL,
so if you don't specify if it can or not be null,
you better stay on the safe side and consider it as nullable.
Then,
I see other problems:
- I don't get a warning from g(), nor from h(), which I'd want.
To get something like const-safety,
we need to design it so that it can be enforced;
either you guarantee that a pointer cannot be NULL (_Nonnull),
or you cannot guarantee it (not _Nonnull).
Otherwise,
[[gnu::nonnull]] would seem better to me.
So, I'd leave out of the design everything but _Nonnull,
and consider everything else as nullable by default.
- I get a warning from f().
Ideally,
a programmer should not need to cast
(casts are dangerous),
to convert a nullable pointer to a _nonnull pointer.
For that,
appropriate checks should be in the preceeding code.
Otherwise, a diagnostic should be issued.
To be on the safe side,
if a compiler has doubts,
it should diagnose.
There's some Clang document that talks about something similar.
I don't know its validity,
or if it was a draft before _Nonnull qualifiers.
<https://clang.llvm.org/docs/analyzer/developer-docs/nullability.html>
Thanks!
Alex
More information about the cfe-dev
mailing list