libc: Implement qualifier-preserving standard library functions

Several standard library functions are specified to return an unqualified
pointer while accepting a pointer to a potentially const-qualified object.
N3020 addresses this behaviour, discarding qualifiers due to incompatible
pointer types, by introducing qualifier-preserving macros for the affected
set of standard library functions.

Add `__qualsel()` helper to `<sys/cdefs.h>`, implemented using the generic
selection, and define qualifier-preserving macros for that set of functions
in `<string.h>`, `<wchar.h>`, and `<stdlib.h>`.

Macros are gated on `_STDC_VERSION__ >= 202311L && !__cplusplus`, therefore
there is no behavioural change for earlier C modes or C++ translation units.
The kernel is likewise unaffected, as it does not include userland headers.

As function-like macros, they are transparent except at a call site where
the address-of operator is applied, the macro is suppressed via `#undef`,
or the identifier appears in parenthesised form; all of which cause the
underlying function designator to be used instead.

Reviewed by:	fuz
Approved by:	fuz (mentor)
MFC after:	1 month
Pull Request:	https://github.com/freebsd/freebsd-src/pull/2288
This commit is contained in:
Faraz Vahedi committed 2026-08-13 20:08:49 +03:30
1 parent 4a8bf4de7b
commit d08296c7ab
13 files changed
+187 -33

No files matched your search

+12
View File
@@ -418,4 +418,16 @@ errno_t qsort_s(void *, rsize_t, rsize_t,
__END_DECLS
__NULLABILITY_PRAGMA_POP
#if defined(__qualsel) && !defined(__cplusplus) && \
defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
#define bsearch(key, base, nmemb, size, compar) __qualsel((base), \
(const void *)(bsearch)((key), (base), (nmemb), (size), (compar)), \
(bsearch)((key), (base), (nmemb), (size), (compar)))
#ifdef __BLOCKS__
#define bsearch_b(key, base, nmemb, size, compar) __qualsel((base), \
(const void *)(bsearch_b)((key), (base), (nmemb), (size), (compar)), \
(bsearch_b)((key), (base), (nmemb), (size), (compar)))
#endif
#endif
#endif /* !_STDLIB_H_ */
+28
View File
@@ -205,4 +205,32 @@ errno_t memset_s(void *, rsize_t, int, rsize_t);
#endif /* __EXT1_VISIBLE */
__END_DECLS
#if defined(__qualsel) && !defined(__cplusplus) && \
defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
#define memchr(b, c, n) __qualsel((b), \
(const void *)(memchr)((b), (c), (n)), (memchr)((b), (c), (n)))
#define strchr(s, c) __qualsel((s), \
(const char *)(strchr)((s), (c)), (strchr)((s), (c)))
#define strpbrk(s, charset) __qualsel((s), \
(const char *)(strpbrk)((s), (charset)), (strpbrk)((s), (charset)))
#define strrchr(s, c) __qualsel((s), \
(const char *)(strrchr)((s), (c)), (strrchr)((s), (c)))
#define strstr(s, find) __qualsel((s), \
(const char *)(strstr)((s), (find)), (strstr)((s), (find)))
#if __BSD_VISIBLE
#define memmem(b, blen, pat, plen) __qualsel((b), \
(const void *)(memmem)((b), (blen), (pat), (plen)), \
(memmem)((b), (blen), (pat), (plen)))
#define memrchr(b, c, n) __qualsel((b), \
(const void *)(memrchr)((b), (c), (n)), (memrchr)((b), (c), (n)))
#define strcasestr(s, find) __qualsel((s), \
(const char *)(strcasestr)((s), (find)), (strcasestr)((s), (find)))
#define strchrnul(s, c) __qualsel((s), \
(const char *)(strchrnul)((s), (c)), (strchrnul)((s), (c)))
#define strnstr(s, find, slen) __qualsel((s), \
(const char *)(strnstr)((s), (find), (slen)), \
(strnstr)((s), (find), (slen)))
#endif /* __BSD_VISIBLE */
#endif
#endif /* _STRING_H_ */
+14
View File
@@ -246,4 +246,18 @@ size_t wcslcpy(wchar_t *, const wchar_t *, size_t);
#endif
__END_DECLS
#if defined(__qualsel) && !defined(__cplusplus) && \
defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
#define wcschr(s, c) __qualsel((s), \
(const wchar_t *)(wcschr)((s), (c)), (wcschr)((s), (c)))
#define wcspbrk(s, set) __qualsel((s), \
(const wchar_t *)(wcspbrk)((s), (set)), (wcspbrk)((s), (set)))
#define wcsrchr(s, c) __qualsel((s), \
(const wchar_t *)(wcsrchr)((s), (c)), (wcsrchr)((s), (c)))
#define wcsstr(s, find) __qualsel((s), \
(const wchar_t *)(wcsstr)((s), (find)), (wcsstr)((s), (find)))
#define wmemchr(s, c, n) __qualsel((s), \
(const wchar_t *)(wmemchr)((s), (c), (n)), (wmemchr)((s), (c), (n)))
#endif
#endif /* !_WCHAR_H_ */
+3 -2
View File
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd July 17, 2019
.Dd June 21, 2026
.Dt BSEARCH 3
.Os
.Sh NAME
@@ -153,4 +153,5 @@ main(void)
The
.Fn bsearch
function conforms to
.St -isoC .
.St -isoC-2023 ,
where it is specified as a qualifier-preserving function.
+7 -2
View File
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd December 5, 2023
.Dd June 21, 2026
.Dt BSTRING 3
.Os
.Sh NAME
@@ -88,7 +88,6 @@ See the specific manual pages for more information.
.Xr memset 3
.Sh STANDARDS
The functions
.Fn memchr ,
.Fn memcmp ,
.Fn memcpy ,
.Fn memmove ,
@@ -96,6 +95,12 @@ and
.Fn memset
conform to
.St -isoC .
.Pp
The
.Fn memchr
function conforms to
.St -isoC-2023 ,
where it is specified as a qualifier-preserving function.
.Sh HISTORY
The functions
.Fn bzero
+9 -4
View File
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd April 9, 2008
.Dd June 21, 2026
.Dt MEMCHR 3
.Os
.Sh NAME
@@ -92,13 +92,18 @@ bytes.
.Sh STANDARDS
The
.Fn memchr
function
conforms to
.St -isoC .
function conforms to
.St -isoC-2023 ,
where it is specified as a qualifier-preserving function.
.Pp
The
.Fn memrchr
function is a GNU extension and conforms to no standard.
Like the qualifier-preserving functions in
.St -isoC-2023 ,
.Fx implements
.Fn memrchr
as qualifier-preserving as well.
.Sh HISTORY
The
.Fn memrchr
+5
View File
@@ -71,6 +71,11 @@ is returned.
.Fn memmem
conforms to
.St -p1003.1-2024 .
Like the qualifier-preserving functions in
.St -isoC-2023 ,
.Fx implements
.Fn memmem
as qualifier-preserving as well.
.Sh HISTORY
The
.Fn memmem
+10 -2
View File
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd February 13, 2013
.Dd June 21, 2026
.Dt STRCHR 3
.Os
.Sh NAME
@@ -109,12 +109,20 @@ The functions
and
.Fn strrchr
conform to
.St -isoC .
.St -isoC-2023 ,
where they are specified as qualifier-preserving functions.
.Pp
The function
.Fn strchrnul
is a
.Tn GNU
extension.
Like the qualifier-preserving functions in
.St -isoC-2023 ,
.Fx
implements
.Fn strchrnul
as qualifier-preserving as well.
.Sh HISTORY
The
.Fn strchrnul
+11 -5
View File
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd September 2, 2023
.Dd June 21, 2026
.Dt STRING 3
.Os
.Sh NAME
@@ -136,20 +136,26 @@ for size limitations.
The
.Fn strcat ,
.Fn strncat ,
.Fn strchr ,
.Fn strrchr ,
.Fn strcmp ,
.Fn strncmp ,
.Fn strcpy ,
.Fn strncpy ,
.Fn strerror ,
.Fn strlen ,
.Fn strpbrk ,
.Fn strspn ,
.Fn strcspn ,
.Fn strstr ,
and
.Fn strtok
functions
conform to
.St -isoC .
.Pp
The
.Fn strchr ,
.Fn strrchr ,
.Fn strpbrk ,
and
.Fn strstr
functions conform to
.St -isoC-2023 ,
where they are specified as qualifier-preserving functions.
+4 -4
View File
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd June 4, 1993
.Dd June 21, 2026
.Dt STRPBRK 3
.Os
.Sh NAME
@@ -69,6 +69,6 @@ returns NULL.
.Sh STANDARDS
The
.Fn strpbrk
function
conforms to
.St -isoC .
function conforms to
.St -isoC-2023 ,
where it is specified as a qualifier-preserving function.
+17 -4
View File
@@ -30,7 +30,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd October 11, 2001
.Dd June 21, 2026
.Dt STRSTR 3
.Os
.Sh NAME
@@ -147,9 +147,22 @@ ptr = strnstr(largestring, smallstring, 4);
.Sh STANDARDS
The
.Fn strstr
function
conforms to
.St -isoC .
function conforms to
.St -isoC-2023 ,
where it is specified as a qualifier-preserving function.
.Pp
The
.Fn strcasestr
function is a
.Tn GNU
extension, and the
.Fn strnstr
function is a
.Bx
extension.
Like the qualifier-preserving functions in
.St -isoC-2023 ,
.Fx implements them as qualifier-preserving as well.
.Sh HISTORY
The
.Fn strnstr
+46 -10
View File
@@ -154,21 +154,57 @@ counterpart, such as
.Xr strspn 3 ,
.Xr strstr 3
.Sh STANDARDS
These functions conform to
.St -isoC-99 ,
with the exception of
Functions
.Fn wmemcmp ,
.Fn wmemcpy ,
.Fn wmemmove ,
.Fn wmemset ,
.Fn wcscat ,
.Fn wcscmp ,
.Fn wcscpy ,
.Fn wcscspn ,
.Fn wcslen ,
.Fn wcsncat ,
.Fn wcsncmp ,
.Fn wcsncpy ,
and
.Fn wcsspn
conform to
.St -isoC-99 .
.Pp
Functions
.Fn wmemchr ,
.Fn wcschr ,
.Fn wcspbrk ,
.Fn wcsrchr ,
and
.Fn wcsstr
conform to
.St -isoC-2023 ,
where they are specified as qualifier-preserving functions.
.Pp
Functions
.Fn wcpcpy ,
.Fn wcpncpy ,
.Fn wcscasecmp ,
.Fn wcsdup ,
.Fn wcsncasecmp ,
and
.Fn wcsnlen ,
which conform to
.St -p1003.1-2008 ;
.Fn wcsnlen
conform to
.St -p1003.1-2008 ,
and functions
.Fn wcslcat
and
.Fn wcslcat ,
.Fn wcslcpy ,
and
.Fn wmempcpy ,
which are extensions.
first introduced by
.Ox ,
conform to
.St -p1003.1-2024 .
.Pp
Function
.Fn wmempcpy
is a
.Tn GNU
extensions.
+21
View File
@@ -227,6 +227,27 @@
__typeof(((void)0, (expr))), t), yes, no)
#endif
/*
* __qualsel() is the building block for C23 qualifier-preserving macros
* as proposed in N3020: it selects cexpr when the pointer expression p
* references a const-qualified object, and expr otherwise.
*
* The conditional operator's composite-type rule collapses every
* pointer-to-const-object type onto the single "const void *"" association,
* so callers passing any such pointer are matched even though _Generic()
* otherwise compares types exactly.
*
* The (__uintptr_t) round-trip only suppresses -Wcast-qual on the throwaway
* second operand.
*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
__has_extension(c_generic_selections)
#define __qualsel(p, cexpr, expr) \
_Generic(1 ? (p) : (void *)(__uintptr_t)(p), \
const void *: (cexpr), \
default: (expr))
#endif
/*
* C99 Static array indices in function parameter declarations. Syntax such as:
* void bar(int myArray[static 10]);