From df17f4f37ed3ca373d23ad42eae51122bdb96626 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 23 Nov 2025 00:50:40 -0800 Subject: [PATCH] Port to C23 qualifier-generic fns like strchr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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(-) --- a/lib/argp-help.c +++ b/lib/argp-help.c @@ -1601,7 +1601,7 @@ argp_doc (const struct argp *argp, const 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; } --- 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 --- a/lib/c-strstr.c +++ b/lib/c-strstr.c @@ -28,5 +28,5 @@ c_strstr (const char *haystack, const ch { /* 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); } --- a/lib/dfa.c +++ b/lib/dfa.c @@ -4069,7 +4069,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; --- 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); } --- a/lib/mbspbrk.c +++ b/lib/mbspbrk.c @@ -90,5 +90,5 @@ mbspbrk (const char *string, const char return NULL; } else - return strpbrk (string, accept); + return (char *) strpbrk (string, accept); } --- 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); } --- a/lib/memchr2.c +++ b/lib/memchr2.c @@ -55,7 +55,7 @@ memchr2 (void const *s, int c1_in, int c 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. */ --- a/lib/stdlib.in.h +++ b/lib/stdlib.in.h @@ -238,9 +238,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 *)) --- a/lib/string-desc.c +++ b/lib/string-desc.c @@ -113,9 +113,9 @@ _sd_index (idx_t s_nbytes, const char *s { 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; } --- a/lib/string.in.h +++ b/lib/string.in.h @@ -403,7 +403,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_CXX (memchr, const void *, void *, (void const *, int, size_t), @@ -655,7 +654,6 @@ _GL_WARN_ON_USE (stpncpy, "stpncpy is un #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), @@ -976,7 +974,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 " @@ -1006,7 +1003,6 @@ _GL_WARN_ON_USE (strspn, "strspn cannot #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), --- a/lib/wchar.in.h +++ b/lib/wchar.in.h @@ -316,7 +316,7 @@ _GL_EXTERN_C int wcsncmp (const wchar_t _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) --- 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, --- a/tests/test-memchr.c +++ b/tests/test-memchr.c @@ -31,7 +31,7 @@ SIGNATURE_CHECK (memchr, void *, (void c 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; --- 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;