Implement C23 const-preserving standard library macros
authorJoseph Myers <josmyers@redhat.com>
Thu, 20 Nov 2025 19:30:27 +0000 (19:30 +0000)
committerJoseph Myers <josmyers@redhat.com>
Thu, 20 Nov 2025 19:31:04 +0000 (19:31 +0000)
C23 makes various standard library functions, that return a pointer
into an input array, into macros that return a pointer to const when
the relevant argument passed to the macro is a pointer to const.  (The
requirement is for macros, with the existing function types applying
when macro expansion is suppressed.  When a null pointer constant is
passed, such as integer 0, that's the same as a pointer to non-const.)

Implement this feature.  This only applies to C, not C++, since such
macros are not an appropriate way of doing this for C++ and all the
affected functions other than bsearch have overloads to implement an
equivalent feature for C++ anyway.  Nothing is done to apply such a
change to any non-C23 functions with the same property of returning a
pointer into an input array.

The feature is also disabled when _LIBC is defined, since there are
various places in glibc that either redefine these identifiers as
macros, or define the functions themselves, and would need changing to
work in the presence of these macro definitions.  A natural question
is whether we should in fact change those places and not disable the
macro definitions for _LIBC.  If so, we'd need a solution for the
places in glibc that define the macro *before* including the relevant
header (in order in effect to disable the header declaration of the
function by renaming that declaration).

One testcase has #undef added to avoid conflicting with this feature
and another has const added; -Wno-discarded-qualifiers is added for
building zic (but could be removed once there's a new upstream tzcode
release that's const-safe with this C23 change and glibc has updated
to code from that new release).  Probably other places in glibc proper
would need const added if we remove the _LIBC conditionals.

Another question would be whether some GCC extension should be added
to support this feature better with macros that only expand each
argument once (as well as reducing duplication of diagnostics for bad
usages such as non-pointer and pointer-to-volatile-qualfied
arguments).

Tested for x86_64.

16 files changed:
NEWS
debug/tst-backtrace.h
manual/search.texi
manual/string.texi
misc/sys/cdefs.h
stdlib/Makefile
stdlib/stdlib.h
stdlib/tst-const.c [new file with mode: 0644]
string/Makefile
string/string.h
string/test-strnlen.c
string/tst-const.c [new file with mode: 0644]
timezone/Makefile
wcsmbs/Makefile
wcsmbs/tst-const.c [new file with mode: 0644]
wcsmbs/wchar.h

diff --git a/NEWS b/NEWS
index 7e1264f448605b9d6131cc23d9fbdf0ada67af8f..92c2b2d78ad372cb176e09b1e0d88cf5161f60aa 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,12 @@ Major new features:
   arguments to support expressions with a comma inside a compound
   literal initializer not surrounded by parentheses.
 
+* For ISO C23, the functions bsearch, memchr, strchr, strpbrk, strrchr,
+  strstr, wcschr, wcspbrk, wcsrchr, wcsstr and wmemchr that return
+  pointers into their input arrays now have definitions as macros that
+  return a pointer to a const-qualified type when the input argument is
+  a pointer to a const-qualified type.
+
 * The C23 typedef names long_double_t, _Float32_t, _Float64_t, and (on
   platforms supporting _Float128) _Float128_t, introduced in TS
   18661-3:2015, have been added to <math.h>.
index e1c0a82777981906544cee616122453d8b57cbcf..a48ac0f4a8e78cc850ad9cc82c9cc66aec3bfb0d 100644 (file)
@@ -43,6 +43,6 @@ volatile int x;
 static inline bool
 match (const char *sym, const char *name)
 {
-  char *p = strchr (sym, '(');
+  const char *p = strchr (sym, '(');
   return p != NULL && strstr (p, name) != NULL;
 }
index cb08c494092ef77f8846617677e33d1de0740ac1..d38e23a620539bbcc7a9491236447ac9b68199cb 100644 (file)
@@ -156,6 +156,14 @@ that matches, the one that is returned is unspecified.
 
 This function derives its name from the fact that it is implemented
 using the binary search algorithm.
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{array} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{bsearch} returns @code{const void *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{void *} regardless.
 @end deftypefun
 
 @node Array Sort Function
index 85318973d88f17bb7d57204e2757e839d566750c..24004f1f5cc60b70bafde0794351734a4eb1ce2b 100644 (file)
@@ -1802,6 +1802,15 @@ This function finds the first occurrence of the byte @var{c} (converted
 to an @code{unsigned char}) in the initial @var{size} bytes of the
 object beginning at @var{block}.  The return value is a pointer to the
 located byte, or a null pointer if no match was found.
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{block} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{memchr} returns @code{const void *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{void *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {wchar_t *} wmemchr (const wchar_t *@var{block}, wchar_t @var{wc}, size_t @var{size})
@@ -1811,6 +1820,15 @@ This function finds the first occurrence of the wide character @var{wc}
 in the initial @var{size} wide characters of the object beginning at
 @var{block}.  The return value is a pointer to the located wide
 character, or a null pointer if no match was found.
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{block} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{wmemchr} returns @code{const wchar_t *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{wchar_t *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {void *} rawmemchr (const void *@var{block}, int @var{c})
@@ -1869,6 +1887,15 @@ When @code{strchr} returns a null pointer, it does not let you know
 the position of the terminating null byte it has found.  If you
 need that information, it is better (but less portable) to use
 @code{strchrnul} than to search for it a second time.
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{string} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{strchr} returns @code{const char *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{char *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {wchar_t *} wcschr (const wchar_t *@var{wstring}, wchar_t @var{wc})
@@ -1884,6 +1911,15 @@ string, so you can use this function get a pointer to the end
 of a wide string by specifying a null wide character as the
 value of the @var{wc} argument.  It would be better (but less portable)
 to use @code{wcschrnul} in this case, though.
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{wstring} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{wcschr} returns @code{const wchar_t *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{wchar_t *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {char *} strchrnul (const char *@var{string}, int @var{c})
@@ -1943,6 +1979,15 @@ For example,
 strrchr ("hello, world", 'l')
     @result{} "ld"
 @end smallexample
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{string} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{strrchr} returns @code{const char *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{char *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {wchar_t *} wcsrchr (const wchar_t *@var{wstring}, wchar_t @var{wc})
@@ -1951,6 +1996,15 @@ strrchr ("hello, world", 'l')
 The function @code{wcsrchr} is like @code{wcschr}, except that it searches
 backwards from the end of the string @var{wstring} (instead of forwards
 from the front).
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{wstring} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{wcsrchr} returns @code{const wchar_t *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{wchar_t *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {char *} strstr (const char *@var{haystack}, const char *@var{needle})
@@ -1969,6 +2023,15 @@ strstr ("hello, world", "l")
 strstr ("hello, world", "wo")
     @result{} "world"
 @end smallexample
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{haystack} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{strstr} returns @code{const char *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{char *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {wchar_t *} wcsstr (const wchar_t *@var{haystack}, const wchar_t *@var{needle})
@@ -1979,6 +2042,15 @@ substring @var{needle} rather than just a single wide character.  It
 returns a pointer into the string @var{haystack} that is the first wide
 character of the substring, or a null pointer if no match was found.  If
 @var{needle} is an empty string, the function returns @var{haystack}.
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{haystack} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{wcsstr} returns @code{const wchar_t *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{wchar_t *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {wchar_t *} wcswcs (const wchar_t *@var{haystack}, const wchar_t *@var{needle})
@@ -2104,6 +2176,15 @@ strpbrk ("hello, world", " \t\n,.;!?")
 In a multibyte string, characters consisting of
 more than one byte are not treated as single entities.  Each byte is treated
 separately.  The function is not locale-dependent.
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{string} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{strpbrk} returns @code{const char *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{char *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 @deftypefun {wchar_t *} wcspbrk (const wchar_t *@var{wstring}, const wchar_t *@var{stopset})
@@ -2114,6 +2195,15 @@ related to @code{wcscspn}, except that it returns a pointer to the first
 wide character in @var{wstring} that is a member of the set
 @var{stopset} instead of the length of the initial substring.  It
 returns a null pointer if no such wide character from @var{stopset} is found.
+
+In ISO C23 and later, this function is qualifier-generic:
+that is, it is also implemented as a function-like macro,
+and when the macro is used and @var{wstring} has a type
+that is a pointer to a @code{const}-qualified object type,
+@code{wcspbrk} returns @code{const wchar_t *}.
+As an obsolescent feature, if the macro is suppressed
+the external function returns @code{wchar_t *} regardless.
+The function is also qualifier-generic in C++.
 @end deftypefun
 
 
index 3e874282df016dfb37589535430a7d0df0fc7bcd..588c7c67c5c16366cffe1aa119ff2e794ce4a03d 100644 (file)
@@ -828,6 +828,18 @@ _Static_assert (0, "IEEE 128-bits long double requires redirection on this platf
 # define __HAVE_GENERIC_SELECTION 0
 #endif
 
+#if __HAVE_GENERIC_SELECTION
+/* If PTR is a pointer to const, return CALL cast to type CTYPE,
+   otherwise return CALL.  Pointers to types with non-const qualifiers
+   are not valid.  This should not be defined for C++, as macros are
+   not an appropriate way of implementing such qualifier-generic
+   operations for C++.  */
+# define __glibc_const_generic(PTR, CTYPE, CALL)       \
+  _Generic (0 ? (PTR) : (void *) 1,                    \
+           const void *: (CTYPE) (CALL),               \
+           default: CALL)
+#endif
+
 #if __GNUC_PREREQ (10, 0)
 /* Designates a 1-based positional argument ref-index of pointer type
    that can be used to access size-index elements of the pointed-to
index 176a79572b0a70383974ea2019559c2b25cf007a..545211dfbff74dbe5010fa75565d6f5de2e312cd 100644 (file)
@@ -280,6 +280,7 @@ tests := \
   tst-canon-bz26341 \
   tst-concurrent-exit \
   tst-concurrent-quick_exit \
+  tst-const \
   tst-cxa_atexit \
   tst-environ \
   tst-environ-change-1 \
index 593e859a4d4bf427002d3268c0b60908ea54dd33..4a79c612de6b929201a4b006dd252c4a20ff7c3f 100644 (file)
@@ -983,6 +983,12 @@ extern void *bsearch (const void *__key, const void *__base,
 # include <bits/stdlib-bsearch.h>
 #endif
 
+#if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+# define bsearch(KEY, BASE, NMEMB, SIZE, COMPAR)                       \
+  __glibc_const_generic (BASE, const void *,                           \
+                        bsearch (KEY, BASE, NMEMB, SIZE, COMPAR))
+#endif
+
 /* Sort NMEMB elements of BASE, of SIZE bytes each,
    using COMPAR to perform the comparisons.  */
 extern void qsort (void *__base, size_t __nmemb, size_t __size,
diff --git a/stdlib/tst-const.c b/stdlib/tst-const.c
new file mode 100644 (file)
index 0000000..89662fd
--- /dev/null
@@ -0,0 +1,55 @@
+/* Test bsearch const-generic macro.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include <libc-diag.h>
+
+void *vp;
+const void *cvp;
+int *ip;
+const int *cip;
+size_t sz;
+int (*compar) (const void *, const void *);
+
+#define CHECK_TYPE(EXPR, TYPE)                                 \
+  _Static_assert (_Generic (EXPR, TYPE: 1), "type check")
+
+static int
+do_test (void)
+{
+  /* This is a compilation test.  */
+  CHECK_TYPE (bsearch (cvp, cvp, sz, sz, compar), const void *);
+  CHECK_TYPE (bsearch (cvp, vp, sz, sz, compar), void *);
+  CHECK_TYPE (bsearch (vp, cvp, sz, sz, compar), const void *);
+  CHECK_TYPE (bsearch (vp, vp, sz, sz, compar), void *);
+  CHECK_TYPE (bsearch (cvp, cip, sz, sz, compar), const void *);
+  CHECK_TYPE (bsearch (cvp, ip, sz, sz, compar), void *);
+  CHECK_TYPE (bsearch (vp, cip, sz, sz, compar), const void *);
+  CHECK_TYPE (bsearch (vp, ip, sz, sz, compar), void *);
+  DIAG_PUSH_NEEDS_COMMENT;
+  /* This deliberately tests the type of the result with a null
+     pointer constant argument.  */
+  DIAG_IGNORE_NEEDS_COMMENT (14, "-Wnonnull");
+  CHECK_TYPE (bsearch (cvp, 0, sz, sz, compar), void *);
+  CHECK_TYPE (bsearch (cvp, (void *) 0, sz, sz, compar), void *);
+  DIAG_POP_NEEDS_COMMENT;
+  CHECK_TYPE ((bsearch) (cvp, cvp, sz, sz, compar), void *);
+  return 0;
+}
+
+#include <support/test-driver.c>
index d842ae04570b88750c7f898b1c9bad04e1c41796..ffa6590b3eece5505eeb4552d9e4d643158524a6 100644 (file)
@@ -196,6 +196,7 @@ tests := \
   tester \
   tst-bswap \
   tst-cmp \
+  tst-const \
   tst-endian \
   tst-inlcall \
   tst-memmove-overflow \
index 81f0b7fd21141bf89dba878f585ad76b92d762b0..1dc5995c17847c269f914d52123bbe54c69a618d 100644 (file)
@@ -113,6 +113,10 @@ memchr (const void *__s, int __c, size_t __n) __THROW
 #else
 extern void *memchr (const void *__s, int __c, size_t __n)
       __THROW __attribute_pure__ __nonnull ((1));
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define memchr(S, C, N)                                      \
+  __glibc_const_generic (S, const void *, memchr (S, C, N))
+# endif
 #endif
 
 #ifdef __USE_GNU
@@ -252,6 +256,10 @@ strchr (const char *__s, int __c) __THROW
 #else
 extern char *strchr (const char *__s, int __c)
      __THROW __attribute_pure__ __nonnull ((1));
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define strchr(S, C)                                         \
+  __glibc_const_generic (S, const char *, strchr (S, C))
+# endif
 #endif
 /* Find the last occurrence of C in S.  */
 #ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
@@ -279,6 +287,10 @@ strrchr (const char *__s, int __c) __THROW
 #else
 extern char *strrchr (const char *__s, int __c)
      __THROW __attribute_pure__ __nonnull ((1));
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define strrchr(S, C)                                                \
+  __glibc_const_generic (S, const char *, strrchr (S, C))
+# endif
 #endif
 
 #ifdef __USE_MISC
@@ -329,6 +341,10 @@ strpbrk (const char *__s, const char *__accept) __THROW
 #else
 extern char *strpbrk (const char *__s, const char *__accept)
      __THROW __attribute_pure__ __nonnull ((1, 2));
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define strpbrk(S, ACCEPT)                                   \
+  __glibc_const_generic (S, const char *, strpbrk (S, ACCEPT))
+# endif
 #endif
 /* Find the first occurrence of NEEDLE in HAYSTACK.  */
 #ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
@@ -356,6 +372,11 @@ strstr (const char *__haystack, const char *__needle) __THROW
 #else
 extern char *strstr (const char *__haystack, const char *__needle)
      __THROW __attribute_pure__ __nonnull ((1, 2));
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define strstr(HAYSTACK, NEEDLE)                     \
+  __glibc_const_generic (HAYSTACK, const char *,       \
+                        strstr (HAYSTACK, NEEDLE))
+# endif
 #endif
 
 
index acb1c055fc1eba776a50700401365f2a00007aa3..7b5ceaf941da00153ed0384c239cc6e2cf5d59c4 100644 (file)
@@ -63,6 +63,7 @@ IMPL (__strnlen_default, 1)
 # define libc_hidden_weak(a)
 # include "wcsmbs/wmemchr.c"
 # define WCSNLEN __wcsnlen_default
+# undef wmemchr
 # define wmemchr __wmemchr_default
 # include "wcsmbs/wcsnlen.c"
 IMPL (__wcsnlen_default, 1)
diff --git a/string/tst-const.c b/string/tst-const.c
new file mode 100644 (file)
index 0000000..a7c8ad5
--- /dev/null
@@ -0,0 +1,107 @@
+/* Test <string.h> const-generic macros.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include <libc-diag.h>
+
+void *vp;
+const void *cvp;
+int *ip;
+const int *cip;
+char *cp;
+const char *ccp;
+int c;
+size_t sz;
+
+#define CHECK_TYPE(EXPR, TYPE)                                 \
+  _Static_assert (_Generic (EXPR, TYPE: 1), "type check")
+
+static int
+do_test (void)
+{
+  /* This is a compilation test.  */
+  CHECK_TYPE (memchr (vp, c, sz), void *);
+  CHECK_TYPE (memchr (cvp, c, sz), const void *);
+  CHECK_TYPE (memchr (ip, c, sz), void *);
+  CHECK_TYPE (memchr (cip, c, sz), const void *);
+  CHECK_TYPE (memchr (cp, c, sz), void *);
+  CHECK_TYPE (memchr (ccp, c, sz), const void *);
+  DIAG_PUSH_NEEDS_COMMENT;
+  /* This deliberately tests the type of the result with a null
+     pointer constant argument.  */
+  DIAG_IGNORE_NEEDS_COMMENT (14, "-Wnonnull");
+  CHECK_TYPE (memchr (0, c, sz), void *);
+  CHECK_TYPE (memchr ((void *) 0, c, sz), void *);
+  DIAG_POP_NEEDS_COMMENT;
+  CHECK_TYPE ((memchr) (cvp, c, sz), void *);
+  CHECK_TYPE (strchr (vp, c), char *);
+  CHECK_TYPE (strchr (cvp, c), const char *);
+  CHECK_TYPE (strchr (cp, c), char *);
+  CHECK_TYPE (strchr (ccp, c), const char *);
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT (14, "-Wnonnull");
+  CHECK_TYPE (strchr (0, c), char *);
+  CHECK_TYPE (strchr ((void *) 0, c), char *);
+  DIAG_POP_NEEDS_COMMENT;
+  CHECK_TYPE ((strchr) (ccp, c), char *);
+  CHECK_TYPE (strpbrk (vp, vp), char *);
+  CHECK_TYPE (strpbrk (vp, cvp), char *);
+  CHECK_TYPE (strpbrk (cvp, vp), const char *);
+  CHECK_TYPE (strpbrk (cvp, cvp), const char *);
+  CHECK_TYPE (strpbrk (cp, cp), char *);
+  CHECK_TYPE (strpbrk (cp, ccp), char *);
+  CHECK_TYPE (strpbrk (ccp, cp), const char *);
+  CHECK_TYPE (strpbrk (ccp, ccp), const char *);
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT (14, "-Wnonnull");
+  CHECK_TYPE (strpbrk (0, cp), char *);
+  CHECK_TYPE (strpbrk (0, ccp), char *);
+  CHECK_TYPE (strpbrk ((void *) 0, cp), char *);
+  CHECK_TYPE (strpbrk ((void *) 0, ccp), char *);
+  DIAG_POP_NEEDS_COMMENT;
+  CHECK_TYPE ((strpbrk) (ccp, ccp), char *);
+  CHECK_TYPE (strrchr (vp, c), char *);
+  CHECK_TYPE (strrchr (cvp, c), const char *);
+  CHECK_TYPE (strrchr (cp, c), char *);
+  CHECK_TYPE (strrchr (ccp, c), const char *);
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT (14, "-Wnonnull");
+  CHECK_TYPE (strrchr (0, c), char *);
+  CHECK_TYPE (strrchr ((void *) 0, c), char *);
+  DIAG_POP_NEEDS_COMMENT;
+  CHECK_TYPE ((strrchr) (ccp, c), char *);
+  CHECK_TYPE (strstr (vp, vp), char *);
+  CHECK_TYPE (strstr (vp, cvp), char *);
+  CHECK_TYPE (strstr (cvp, vp), const char *);
+  CHECK_TYPE (strstr (cvp, cvp), const char *);
+  CHECK_TYPE (strstr (cp, cp), char *);
+  CHECK_TYPE (strstr (cp, ccp), char *);
+  CHECK_TYPE (strstr (ccp, cp), const char *);
+  CHECK_TYPE (strstr (ccp, ccp), const char *);
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT (14, "-Wnonnull");
+  CHECK_TYPE (strstr (0, cp), char *);
+  CHECK_TYPE (strstr (0, ccp), char *);
+  CHECK_TYPE (strstr ((void *) 0, cp), char *);
+  CHECK_TYPE (strstr ((void *) 0, ccp), char *);
+  DIAG_POP_NEEDS_COMMENT;
+  CHECK_TYPE ((strstr) (ccp, ccp), char *);
+  return 0;
+}
+
+#include <support/test-driver.c>
index 6c7a7d4f4d4a4f8f29d1e4bf61e6c24f3f5183e1..c03a68962c84f6825df254640b8eb348f9a81380 100644 (file)
@@ -73,8 +73,10 @@ tz-cflags = -DTZDIR='"$(zonedir)"' \
 # The -Wno-unused-variable flag is used to prevent GCC 6
 # from warning about time_t_min and time_t_max which are
 # defined in private.h but not used.
+# -Wno-discarded-qualifiers is because zic is not prepared for C23
+# -qualifier-generic strchr.
 CFLAGS-zdump.c += $(tz-cflags)
-CFLAGS-zic.c += $(tz-cflags) -Wno-unused-variable
+CFLAGS-zic.c += $(tz-cflags) -Wno-unused-variable -Wno-discarded-qualifiers
 
 # We have to make sure the data for testing the tz functions is available.
 # Don't add leapseconds here since test-tz made checks that work only without
index 559438c1ce8546895e6cee95369613f829fd5d21..71b5ffe1bd5dc9e45d08cbd68138742585aa207e 100644 (file)
@@ -172,6 +172,7 @@ tests := \
   tst-c16-surrogate \
   tst-c16c32-1 \
   tst-c32-state \
+  tst-const \
   tst-fgetwc-after-eof \
   tst-mbrtowc \
   tst-mbrtowc2 \
diff --git a/wcsmbs/tst-const.c b/wcsmbs/tst-const.c
new file mode 100644 (file)
index 0000000..6bd8afc
--- /dev/null
@@ -0,0 +1,85 @@
+/* Test <wchar.h> const-generic macros.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <wchar.h>
+
+void *vp;
+const void *cvp;
+wchar_t *wp;
+const wchar_t *cwp;
+size_t sz;
+wchar_t wc;
+
+#define CHECK_TYPE(EXPR, TYPE)                                 \
+  _Static_assert (_Generic (EXPR, TYPE: 1), "type check")
+
+static int
+do_test (void)
+{
+  /* This is a compilation test.  */
+  CHECK_TYPE (wmemchr (vp, wc, sz), wchar_t *);
+  CHECK_TYPE (wmemchr (cvp, wc, sz), const wchar_t *);
+  CHECK_TYPE (wmemchr (wp, wc, sz), wchar_t *);
+  CHECK_TYPE (wmemchr (cwp, wc, sz), const wchar_t *);
+  CHECK_TYPE (wmemchr (0, wc, sz), wchar_t *);
+  CHECK_TYPE (wmemchr ((void *) 0, wc, sz), wchar_t *);
+  CHECK_TYPE ((wmemchr) (cwp, wc, sz), wchar_t *);
+  CHECK_TYPE (wcschr (vp, wc), wchar_t *);
+  CHECK_TYPE (wcschr (cvp, wc), const wchar_t *);
+  CHECK_TYPE (wcschr (wp, wc), wchar_t *);
+  CHECK_TYPE (wcschr (cwp, wc), const wchar_t *);
+  CHECK_TYPE (wcschr (0, wc), wchar_t *);
+  CHECK_TYPE (wcschr ((void *) 0, wc), wchar_t *);
+  CHECK_TYPE ((wcschr) (cwp, wc), wchar_t *);
+  CHECK_TYPE (wcspbrk (vp, vp), wchar_t *);
+  CHECK_TYPE (wcspbrk (vp, cvp), wchar_t *);
+  CHECK_TYPE (wcspbrk (cvp, vp), const wchar_t *);
+  CHECK_TYPE (wcspbrk (cvp, cvp), const wchar_t *);
+  CHECK_TYPE (wcspbrk (wp, wp), wchar_t *);
+  CHECK_TYPE (wcspbrk (wp, cwp), wchar_t *);
+  CHECK_TYPE (wcspbrk (cwp, wp), const wchar_t *);
+  CHECK_TYPE (wcspbrk (cwp, cwp), const wchar_t *);
+  CHECK_TYPE (wcspbrk (0, wp), wchar_t *);
+  CHECK_TYPE (wcspbrk (0, cwp), wchar_t *);
+  CHECK_TYPE (wcspbrk ((void *) 0, wp), wchar_t *);
+  CHECK_TYPE (wcspbrk ((void *) 0, cwp), wchar_t *);
+  CHECK_TYPE ((wcspbrk) (cwp, cwp), wchar_t *);
+  CHECK_TYPE (wcsrchr (vp, wc), wchar_t *);
+  CHECK_TYPE (wcsrchr (cvp, wc), const wchar_t *);
+  CHECK_TYPE (wcsrchr (wp, wc), wchar_t *);
+  CHECK_TYPE (wcsrchr (cwp, wc), const wchar_t *);
+  CHECK_TYPE (wcsrchr (0, wc), wchar_t *);
+  CHECK_TYPE (wcsrchr ((void *) 0, wc), wchar_t *);
+  CHECK_TYPE ((wcsrchr) (cwp, wc), wchar_t *);
+  CHECK_TYPE (wcsstr (vp, vp), wchar_t *);
+  CHECK_TYPE (wcsstr (vp, cvp), wchar_t *);
+  CHECK_TYPE (wcsstr (cvp, vp), const wchar_t *);
+  CHECK_TYPE (wcsstr (cvp, cvp), const wchar_t *);
+  CHECK_TYPE (wcsstr (wp, wp), wchar_t *);
+  CHECK_TYPE (wcsstr (wp, cwp), wchar_t *);
+  CHECK_TYPE (wcsstr (cwp, wp), const wchar_t *);
+  CHECK_TYPE (wcsstr (cwp, cwp), const wchar_t *);
+  CHECK_TYPE (wcsstr (0, wp), wchar_t *);
+  CHECK_TYPE (wcsstr (0, cwp), wchar_t *);
+  CHECK_TYPE (wcsstr ((void *) 0, wp), wchar_t *);
+  CHECK_TYPE (wcsstr ((void *) 0, cwp), wchar_t *);
+  CHECK_TYPE ((wcsstr) (cwp, cwp), wchar_t *);
+  return 0;
+}
+
+#include <support/test-driver.c>
index b31ca2d241a9d36fd4feb8a639f45d1955046ed5..19f71f90cfcf361863263eee3018fec84fd1379b 100644 (file)
@@ -188,6 +188,10 @@ extern "C++" const wchar_t *wcschr (const wchar_t *__wcs, wchar_t __wc)
 #else
 extern wchar_t *wcschr (const wchar_t *__wcs, wchar_t __wc)
      __THROW __attribute_pure__;
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define wcschr(WCS, WC)                                              \
+  __glibc_const_generic (WCS, const wchar_t *, wcschr (WCS, WC))
+# endif
 #endif
 /* Find the last occurrence of WC in WCS.  */
 #ifdef __CORRECT_ISO_CPP_WCHAR_H_PROTO
@@ -198,6 +202,10 @@ extern "C++" const wchar_t *wcsrchr (const wchar_t *__wcs, wchar_t __wc)
 #else
 extern wchar_t *wcsrchr (const wchar_t *__wcs, wchar_t __wc)
      __THROW __attribute_pure__;
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define wcsrchr(WCS, WC)                                             \
+  __glibc_const_generic (WCS, const wchar_t *, wcsrchr (WCS, WC))
+# endif
 #endif
 
 #ifdef __USE_GNU
@@ -225,6 +233,10 @@ extern "C++" const wchar_t *wcspbrk (const wchar_t *__wcs,
 #else
 extern wchar_t *wcspbrk (const wchar_t *__wcs, const wchar_t *__accept)
      __THROW __attribute_pure__;
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define wcspbrk(WCS, ACCEPT)                                         \
+  __glibc_const_generic (WCS, const wchar_t *, wcspbrk (WCS, ACCEPT))
+# endif
 #endif
 /* Find the first occurrence of NEEDLE in HAYSTACK.  */
 #ifdef __CORRECT_ISO_CPP_WCHAR_H_PROTO
@@ -236,6 +248,11 @@ extern "C++" const wchar_t *wcsstr (const wchar_t *__haystack,
 #else
 extern wchar_t *wcsstr (const wchar_t *__haystack, const wchar_t *__needle)
      __THROW __attribute_pure__;
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define wcsstr(HAYSTACK, NEEDLE)                     \
+  __glibc_const_generic (HAYSTACK, const wchar_t *,    \
+                        wcsstr (HAYSTACK, NEEDLE))
+# endif
 #endif
 
 /* Divide WCS into tokens separated by characters in DELIM.  */
@@ -277,6 +294,10 @@ extern "C++" const wchar_t *wmemchr (const wchar_t *__s, wchar_t __c,
 #else
 extern wchar_t *wmemchr (const wchar_t *__s, wchar_t __c, size_t __n)
      __THROW __attribute_pure__;
+# if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined _LIBC
+#  define wmemchr(S, C, N)                                     \
+  __glibc_const_generic (S, const wchar_t *, wmemchr (S, C, N))
+# endif
 #endif
 
 /* Compare N wide characters of S1 and S2.  */
This page took 0.155985 seconds and 5 git commands to generate.