[cfe-dev] ISO C3X proposal: nonnull qualifier
Alejandro Colomar (man-pages) via cfe-dev
cfe-dev at lists.llvm.org
Thu Dec 2 12:24:34 PST 2021
On 11/16/21 13:34, Alejandro Colomar (man-pages) wrote:
> $ 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;
> }
>
> ---
> 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.
Getting warnings from h() and g() would be easy to impose. Just forbid
implicit addition of _Nonnull qualifier.
>
> - 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>
I came with an idea that would make this QoI, so that the ISO standard
wouldn't need to force every compiler to be flow-sensitive, which would
be a bad precedent.
Implicit additions of the qualifier could be allowed to be warned
_always_ by the standard.
As an extension, quality compilers may not warn when they can see a NULL
check just before the assignment.
User code could make of the following macro, to avoid having to add
casts everywhere, which would be as bad as not having _Nonnull at all.
I got the idea from the GCC __builtin_xxx_overflow() builtins.
#if defined(__cplusplus)
#define const_cast(t, x) const_cast<t>(x)
#else
#define const_cast(t, x) ((t) (x))
#endif
#if !defined(cplusplus)
#define auto __auto_type
#endif
#define nonnull_assign(nn, p) \
({ \
auto p_ = p; \
auto nn_ = nn; \
\
if (p_ == NULL) \
*nn_ = const_cast(typeof(nn_), p_); \
\
p_ == NULL; \
})
And use it like this:
int *_Nonnull f(int *_Nullable p)
{
int *_Nonnull q;
if (nonnull_assign(&q, p))
exit(1);
return q;
}
That way there's only one place where there's a cast, so it can be
easily audited. The above would be warning-free, without requiring a
lot of complexity into the compiler.
Please, forgive the extensive use of GNU extensions in the above snippet
(and the redefinition of auto), as I forgive those who don't use them :)
So, now this can be made non-flow-sensitive, which was a big concern.
And now the biggest concern I can see is that this qualifier works
opposite to const (here discarding is allowed but not adding it), and
that is contrary to how compilers have been working for now. As far as
I could read in the standard, there's no mention to qualifiers being
dropped in an rvalue; Joseph, could you please confirm? Also, as I
already mentioned, Clang already implements this somehow without
discarding the _Nonnull qualifier, so I guess this can be done.
Cheers,
Alex
--
Alejandro Colomar
Linux man-pages comaintainer; http://www.kernel.org/doc/man-pages/
More information about the cfe-dev
mailing list