[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