Hi!

The stddef.h header for C23 defines __STDC_VERSION_STDDEF_H__ and
unreachable macros multiple times in some cases.
The header doesn't have normal multiple inclusion guard, because it supports
for glibc inclusion with __need_{size_t,wchar_t,ptrdiff_t,wint_t,NULL}.
While the definition of __STDC_VERSION_STDDEF_H__ and unreachable is done
solely in the #ifdef _STDDEF_H part, so they are defined only if stddef.h
is included without those __need_* macros defined.  But actually once
stddef.h is included without the __need_* macros, _STDDEF_H is then defined
and while further stddef.h includes without __need_* macros don't do
anything:
#if (!defined(_STDDEF_H) && !defined(_STDDEF_H_) && !defined(_ANSI_STDDEF_H) \
     && !defined(__STDDEF_H__)) \
    || defined(__need_wchar_t) || defined(__need_size_t) \
    || defined(__need_ptrdiff_t) || defined(__need_NULL) \
    || defined(__need_wint_t)
if one includes whole stddef.h first and then stddef.h with some of the
__need_* macros defined, the #ifdef _STDDEF_H part is used again.
It isn't that big deal for most cases, as it uses extra guarding macros
like:
#ifndef _GCC_MAX_ALIGN_T
#define _GCC_MAX_ALIGN_T
...
#endif
etc., but for __STDC_VERSION_STDDEF_H__/unreachable nothing like that is
used.

So, either we do what the following patch does and just don't define
__STDC_VERSION_STDDEF_H__/unreachable second time, or use #ifndef
unreachable separately for the #define unreachable() case, or use
new _GCC_STDC_VERSION_STDDEF_H macro to guard this (or two, one for
__STDC_VERSION_STDDEF_H__ and one for unreachable), or rework the initial
condition to be just
#if !defined(_STDDEF_H) && !defined(_STDDEF_H_) && !defined(_ANSI_STDDEF_H) \
    && !defined(__STDDEF_H__)
- I really don't understand why the header should do anything at all after
it has been included once without __need_* macros.  But changing how this
behaves after 35 years might be risky for various OS/libc combinations.

In any case, the following patch has been bootstrapped/regtested on
x86_64-linux and i686-linux, ok for trunk?  Or something else?

2025-02-26  Jakub Jelinek  <ja...@redhat.com>

        PR c/114870
        * ginclude/stddef.h (__STDC_VERSION_STDDEF_H__, unreachable): Don't
        redefine multiple times if stddef.h is first included without __need_*
        defines and later with them.  Move nullptr_t and unreachable and
        __STDC_VERSION_STDDEF_H__ definitions into the same
        defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L #if block.

        * gcc.dg/c23-stddef-2.c: New test.

--- gcc/ginclude/stddef.h.jj    2025-01-02 11:47:29.191236622 +0100
+++ gcc/ginclude/stddef.h       2025-02-25 18:41:19.755634248 +0100
@@ -444,18 +444,16 @@ typedef struct {
 #endif
 #endif /* C++11.  */
 
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L)
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L
 #ifndef _GCC_NULLPTR_T
 #define _GCC_NULLPTR_T
   typedef __typeof__(nullptr) nullptr_t;
-/* ??? This doesn't define __STDC_VERSION_STDDEF_H__ yet.  */
 #endif
-#endif /* C23.  */
-
-#if defined __STDC_VERSION__ && __STDC_VERSION__ > 201710L
+#ifndef __STDC_VERSION_STDDEF_H__
 #define unreachable() (__builtin_unreachable ())
 #define __STDC_VERSION_STDDEF_H__      202311L
 #endif
+#endif /* C23.  */
 
 #endif /* _STDDEF_H was defined this time */
 
--- gcc/testsuite/gcc.dg/c23-stddef-2.c.jj      2025-02-25 19:04:36.931290359 
+0100
+++ gcc/testsuite/gcc.dg/c23-stddef-2.c 2025-02-25 19:05:46.575324915 +0100
@@ -0,0 +1,17 @@
+/* Test __STDC_VERSION_STDDEF_H__ in C23.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c23 -pedantic-errors -Wsystem-headers" } */
+
+#include <stddef.h>
+/* Simulate what glibc <time.h> does without actually including it.  */
+#define __need_size_t
+#define __need_NULL
+#include <stddef.h>
+
+#ifndef __STDC_VERSION_STDDEF_H__
+#error "__STDC_VERSION_STDDEF_H__ not defined"
+#endif
+
+#if __STDC_VERSION_STDDEF_H__ != 202311L
+#error "bad value of __STDC_VERSION_STDDEF_H__"
+#endif

        Jakub

Reply via email to