This ports Gnulib to strict C23 platforms that reject code like ‘char *q = strchr (P, 'x');’ when P is a pointer to const, because in C23 strchr is a qualifier-generic function so strchr (P, 'x') returns char const *. This patch does not attempt to do the following two things, which might be useful in the future: 1. When compiling on non-C23 platforms, check user code for portability to platforms that define qualifier-generic functions. 2. Port Gnulib to platforms that have qualifier-generic functions not listed in the C23 standard, e.g., strchrnul. I don’t know of any such platforms. * lib/argp-help.c (argp_doc): * lib/c-strstr.c (c_strstr): * lib/dfa.c (comsubs): * lib/mbschr.c (mbschr): * lib/mbspbrk.c (mbspbrk): * lib/mbsrchr.c (mbsrchr): * lib/memchr2.c (memchr2): * lib/string-desc.c (_sd_index): * tests/test-bsearch.c (lib_bsearch): * tests/test-memchr.c (lib_memchr): * tests/test-wmemchr.c (lib_wmemchr): Port to C23, where functions like strchr are qualifier-generic. * lib/c++defs.h (_GL_FUNCDECL_SYS_NAME): New macro. * lib/c++defs.h (_GL_FUNCDECL_SYS): * lib/stdlib.in.h (bsearch): Use it, to prevent C23 names like strchr from acting like macros. * lib/string.in.h (memchr, strchr, strpbrk, strrchr): Do not #undef when GNULIB_POSIXCHECK is defined, as this could cause conforming C23 code to fail to conform. It’s not clear why _GL_WARN_ON_USE_CXX; perhaps it was needed but isn’t any more? But for now, limit the removal of #undef to these four functions where #undeffing is clearly undesirable in C23. * lib/wchar.in.h (wmemchr): Parenthesize function name in decl, to prevent it from acting like a macro. --- ChangeLog | 40 ++++++++++++++++++++++++++++++++++++++++ lib/argp-help.c | 2 +- lib/c++defs.h | 12 +++++++++++- lib/c-strstr.c | 2 +- lib/dfa.c | 2 +- lib/mbschr.c | 2 +- lib/mbspbrk.c | 2 +- lib/mbsrchr.c | 2 +- lib/memchr2.c | 2 +- lib/stdlib.in.h | 6 +++--- lib/string-desc.c | 4 ++-- lib/string.in.h | 4 ---- lib/wchar.in.h | 2 +- tests/test-bsearch.c | 2 +- tests/test-memchr.c | 2 +- tests/test-wmemchr.c | 2 +- 16 files changed, 67 insertions(+), 21 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 4eadd88f12..1cfaf9e528 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +2025-11-23 Paul Eggert <[email protected]> + + Port to C23 qualifier-generic fns like strchr + This ports Gnulib to strict C23 platforms that reject code + like ‘char *q = strchr (P, 'x');’ when P is a pointer to const, + because in C23 strchr is a qualifier-generic function so + strchr (P, 'x') returns char const *. + This patch does not attempt to do the following two things, + which might be useful in the future: + 1. When compiling on non-C23 platforms, check user code for + portability to platforms that define qualifier-generic functions. + 2. Port Gnulib to platforms that have qualifier-generic functions + not listed in the C23 standard, e.g., strchrnul. I don’t know + of any such platforms. + * lib/argp-help.c (argp_doc): + * lib/c-strstr.c (c_strstr): + * lib/dfa.c (comsubs): + * lib/mbschr.c (mbschr): + * lib/mbspbrk.c (mbspbrk): + * lib/mbsrchr.c (mbsrchr): + * lib/memchr2.c (memchr2): + * lib/string-desc.c (_sd_index): + * tests/test-bsearch.c (lib_bsearch): + * tests/test-memchr.c (lib_memchr): + * tests/test-wmemchr.c (lib_wmemchr): + Port to C23, where functions like strchr are qualifier-generic. + * lib/c++defs.h (_GL_FUNCDECL_SYS_NAME): New macro. + * lib/c++defs.h (_GL_FUNCDECL_SYS): + * lib/stdlib.in.h (bsearch): + Use it, to prevent C23 names like strchr from acting like macros. + * lib/string.in.h (memchr, strchr, strpbrk, strrchr): + Do not #undef when GNULIB_POSIXCHECK is defined, as this could + cause conforming C23 code to fail to conform. It’s not clear why + #undef is needed before others uses of _GL_WARN_ON_USE and + _GL_WARN_ON_USE_CXX; perhaps it was needed but isn’t any more? + But for now, limit the removal of #undef to these four functions + where #undeffing is clearly undesirable in C23. + * lib/wchar.in.h (wmemchr): Parenthesize function name in decl, + to prevent it from acting like a macro. + 2025-11-20 Bruno Haible <[email protected]> strnlen: Fix compilation error (regression 2025-11-18). diff --git a/lib/argp-help.c b/lib/argp-help.c index f742e1870f..5bf4d3aacf 100644 --- a/lib/argp-help.c +++ b/lib/argp-help.c @@ -1601,7 +1601,7 @@ argp_doc (const struct argp *argp, const struct argp_state *state, if (doc) { - char *vt = strchr (doc, '\v'); + char const *vt = strchr (doc, '\v'); inp_text = post ? (vt ? vt + 1 : NULL) : doc; inp_text_limit = (!post && vt) ? (vt - doc) : 0; } diff --git a/lib/c++defs.h b/lib/c++defs.h index b77979a325..7384457432 100644 --- a/lib/c++defs.h +++ b/lib/c++defs.h @@ -127,6 +127,16 @@ #define _GL_FUNCDECL_RPL_1(rpl_func,rettype,parameters,...) \ _GL_EXTERN_C_FUNC __VA_ARGS__ rettype rpl_func parameters +/* _GL_FUNCDECL_SYS_NAME (func) expands to plain func if C++, and to + parenthsized func otherwise. Parenthesization is needed in C23 if + the function is like strchr and so is a qualifier-generic macro + that expands to something more complicated. */ +#ifdef __cplusplus +# define _GL_FUNCDECL_SYS_NAME(func) func +#else +# define _GL_FUNCDECL_SYS_NAME(func) (func) +#endif + /* _GL_FUNCDECL_SYS (func, rettype, parameters, [attributes]); declares the system function, named func, with the given prototype, consisting of return type, parameters, and attributes. @@ -139,7 +149,7 @@ _GL_FUNCDECL_SYS (posix_openpt, int, (int flags), _GL_ATTRIBUTE_NODISCARD); */ #define _GL_FUNCDECL_SYS(func,rettype,parameters,...) \ - _GL_EXTERN_C_FUNC __VA_ARGS__ rettype func parameters + _GL_EXTERN_C_FUNC __VA_ARGS__ rettype _GL_FUNCDECL_SYS_NAME (func) parameters /* _GL_CXXALIAS_RPL (func, rettype, parameters); declares a C++ alias called GNULIB_NAMESPACE::func diff --git a/lib/c-strstr.c b/lib/c-strstr.c index 36ee9d29ca..660da96acd 100644 --- a/lib/c-strstr.c +++ b/lib/c-strstr.c @@ -28,5 +28,5 @@ c_strstr (const char *haystack, const char *needle) { /* POSIX says that strstr() interprets the strings as byte sequences, not as character sequences in the current locale. */ - return strstr (haystack, needle); + return (char *) strstr (haystack, needle); } diff --git a/lib/dfa.c b/lib/dfa.c index 34b7f8bf2d..11309ab36b 100644 --- a/lib/dfa.c +++ b/lib/dfa.c @@ -4050,7 +4050,7 @@ comsubs (char *left, char const *right) for (char *lcp = left; *lcp != '\0'; lcp++) { idx_t len = 0; - char *rcp = strchr (right, *lcp); + char const *rcp = strchr (right, *lcp); while (rcp != NULL) { idx_t i; diff --git a/lib/mbschr.c b/lib/mbschr.c index c9e14b5baa..65821340df 100644 --- a/lib/mbschr.c +++ b/lib/mbschr.c @@ -65,5 +65,5 @@ mbschr (const char *string, int c) return NULL; } else - return strchr (string, c); + return (char *) strchr (string, c); } diff --git a/lib/mbspbrk.c b/lib/mbspbrk.c index 3331d70e5c..974efe3a29 100644 --- a/lib/mbspbrk.c +++ b/lib/mbspbrk.c @@ -90,5 +90,5 @@ mbspbrk (const char *string, const char *accept) return NULL; } else - return strpbrk (string, accept); + return (char *) strpbrk (string, accept); } diff --git a/lib/mbsrchr.c b/lib/mbsrchr.c index f1cd8dccaf..3defe676dd 100644 --- a/lib/mbsrchr.c +++ b/lib/mbsrchr.c @@ -64,5 +64,5 @@ mbsrchr (const char *string, int c) return (char *) result; } else - return strrchr (string, c); + return (char *) strrchr (string, c); } diff --git a/lib/memchr2.c b/lib/memchr2.c index c6e7f4e9dc..8ad96500bd 100644 --- a/lib/memchr2.c +++ b/lib/memchr2.c @@ -55,7 +55,7 @@ memchr2 (void const *s, int c1_in, int c2_in, size_t n) c2 = (unsigned char) c2_in; if (c1 == c2) - return memchr (s, c1, n); + return (void *) memchr (s, c1, n); /* Handle the first few bytes by reading one byte at a time. Do this until VOID_PTR is aligned on a longword boundary. */ diff --git a/lib/stdlib.in.h b/lib/stdlib.in.h index bef0aaaf92..fd0e1e0d29 100644 --- a/lib/stdlib.in.h +++ b/lib/stdlib.in.h @@ -224,9 +224,9 @@ _GL_INLINE_HEADER_BEGIN /* Declarations for ISO C N3322. */ #if defined __GNUC__ && __GNUC__ >= 15 && !defined __clang__ -_GL_EXTERN_C void *bsearch (const void *__key, - const void *__base, size_t __nmemb, size_t __size, - int (*__compare) (const void *, const void *)) +_GL_EXTERN_C void *_GL_FUNCDECL_SYS_NAME (bsearch) + (const void *__key, const void *__base, size_t __nmemb, size_t __size, + int (*__compare) (const void *, const void *)) _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 3) _GL_ARG_NONNULL ((5)); _GL_EXTERN_C void qsort (void *__base, size_t __nmemb, size_t __size, int (*__compare) (const void *, const void *)) diff --git a/lib/string-desc.c b/lib/string-desc.c index 2d13a866b9..4113aba865 100644 --- a/lib/string-desc.c +++ b/lib/string-desc.c @@ -110,9 +110,9 @@ _sd_index (idx_t s_nbytes, const char *s_data, char c) { if (s_nbytes > 0) { - void *found = memchr (s_data, (unsigned char) c, s_nbytes); + char const *found = memchr (s_data, (unsigned char) c, s_nbytes); if (found != NULL) - return (char *) found - s_data; + return found - s_data; } return -1; } diff --git a/lib/string.in.h b/lib/string.in.h index fdcdd21bed..8b56acfb51 100644 --- a/lib/string.in.h +++ b/lib/string.in.h @@ -409,7 +409,6 @@ _GL_CXXALIASWARN1 (memchr, void const *, _GL_CXXALIASWARN (memchr); # endif #elif defined GNULIB_POSIXCHECK -# undef memchr /* Assume memchr is always declared. */ _GL_WARN_ON_USE (memchr, "memchr has platform-specific bugs - " "use gnulib module memchr for portability" ); @@ -674,7 +673,6 @@ _GL_WARN_ON_USE (stpncpy, "stpncpy is unportable - " #if defined GNULIB_POSIXCHECK /* strchr() does not work with multibyte strings if the locale encoding is GB18030 and the character to be searched is a digit. */ -# undef strchr /* Assume strchr is always declared. */ _GL_WARN_ON_USE_CXX (strchr, const char *, char *, (const char *, int), @@ -981,7 +979,6 @@ _GL_CXXALIASWARN (strpbrk); Even in this simple case, it does not work with multibyte strings if the locale encoding is GB18030 and one of the characters to be searched is a digit. */ -# undef strpbrk _GL_WARN_ON_USE_CXX (strpbrk, const char *, char *, (const char *, const char *), "strpbrk cannot work correctly on character strings " @@ -1011,7 +1008,6 @@ _GL_WARN_ON_USE (strspn, "strspn cannot work correctly on character strings " #if defined GNULIB_POSIXCHECK /* strrchr() does not work with multibyte strings if the locale encoding is GB18030 and the character to be searched is a digit. */ -# undef strrchr /* Assume strrchr is always declared. */ _GL_WARN_ON_USE_CXX (strrchr, const char *, char *, (const char *, int), diff --git a/lib/wchar.in.h b/lib/wchar.in.h index ab602a2811..6be45155f3 100644 --- a/lib/wchar.in.h +++ b/lib/wchar.in.h @@ -301,7 +301,7 @@ _GL_EXTERN_C int wcsncmp (const wchar_t *__s1, const wchar_t *__s2, size_t __n) _GL_ATTRIBUTE_NONNULL_IF_NONZERO (1, 3) _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 3); # ifndef __cplusplus -_GL_EXTERN_C wchar_t *wmemchr (const wchar_t *__s, wchar_t __wc, size_t __n) +_GL_EXTERN_C wchar_t *(wmemchr) (const wchar_t *__s, wchar_t __wc, size_t __n) _GL_ATTRIBUTE_NONNULL_IF_NONZERO (1, 3); # endif _GL_EXTERN_C wchar_t *wmemset (wchar_t *__s, wchar_t __wc, size_t __n) diff --git a/tests/test-bsearch.c b/tests/test-bsearch.c index aad3a22807..2e9575cc41 100644 --- a/tests/test-bsearch.c +++ b/tests/test-bsearch.c @@ -26,7 +26,7 @@ static void * lib_bsearch (void const *key, void const *base, size_t nel, size_t width, int (*compar) (void const *, void const *)) { - return bsearch (key, base, nel, width, compar); + return (void *) bsearch (key, base, nel, width, compar); } static void *(*volatile volatile_bsearch) (void const *, void const *, size_t, size_t, diff --git a/tests/test-memchr.c b/tests/test-memchr.c index 02b882a975..ef04790f90 100644 --- a/tests/test-memchr.c +++ b/tests/test-memchr.c @@ -31,7 +31,7 @@ SIGNATURE_CHECK (memchr, void *, (void const *, int, size_t)); static void * lib_memchr (void const *s, int c, size_t n) { - return memchr (s, c, n); + return (void *) memchr (s, c, n); } static void *(*volatile volatile_memchr) (void const *, int, size_t) = lib_memchr; diff --git a/tests/test-wmemchr.c b/tests/test-wmemchr.c index c5e8ce541c..457ed76a9b 100644 --- a/tests/test-wmemchr.c +++ b/tests/test-wmemchr.c @@ -27,7 +27,7 @@ static wchar_t * lib_wmemchr (wchar_t const *s, wchar_t wc, size_t n) { - return wmemchr (s, wc, n); + return (wchar_t *) wmemchr (s, wc, n); } static wchar_t *(*volatile volatile_wmemchr) (wchar_t const *, wchar_t, size_t) = lib_wmemchr; -- 2.51.0
