mailing list of musl libc
 help / color / mirror / code / Atom feed
* [musl] [PATCH v1 0/1] include/string.h: Implement QChar wrappers standardized in C23
@ 2025-11-25 13:51 Alejandro Colomar
  2025-11-25 13:51 ` [musl] [PATCH v1 1/1] " Alejandro Colomar
                   ` (4 more replies)
  0 siblings, 5 replies; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-25 13:51 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar

Hi!

I've implemented the QChar const-correct macro wrappers around string
APIs.  These were standardized in ISO C23.

I haven't implemented the QVoid ones, as they're more difficult to
implement.

Here's a small test I used to verify that this works.

	alx@devuan:~/tmp$ cat strchr.c 
	#include <stdio.h>
	#include <string.h>

	int
	main(int argc, const char *argv[argc + 1])
	{
		char *p;

		p = strchr(argv[0], 'u');
		puts(p);

		const char *cp;

		cp = strchr(argv[0], 'u');
		puts(cp);
	}
	alx@devuan:~/tmp$ gcc strchr.c 
	alx@devuan:~/tmp$ ./a.out 
	ut
	ut
	alx@devuan:~/tmp$ gcc -I /opt/local/musl/libc/qchar/include/ strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:9:11: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
	    9 |         p = strchr(argv[0], 'u');
	      |           ^
	alx@devuan:~/tmp$ ./a.out 
	ut
	ut


Have a lovely day!
Alex

Alejandro Colomar (1):
  include/string.h: Implement QChar wrappers standardized in C23

 include/string.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

Range-diff against v0:
-:  -------- > 1:  3cf2501c include/string.h: Implement QChar wrappers standardized in C23
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v1 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-25 13:51 [musl] [PATCH v1 0/1] include/string.h: Implement QChar wrappers standardized in C23 Alejandro Colomar
@ 2025-11-25 13:51 ` Alejandro Colomar
  2025-11-25 21:49 ` [musl] [PATCH v2 0/1] " Alejandro Colomar
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-25 13:51 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/string.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/include/string.h b/include/string.h
index 83e2b946..0da28361 100644
--- a/include/string.h
+++ b/include/string.h
@@ -24,6 +24,16 @@ extern "C" {
 
 #include <bits/alltypes.h>
 
+#define __QCharof(s)  typeof                                            \
+(                                                                     \
+	_Generic(s,                                                   \
+		const char *:  (const char){},                        \
+		const void *:  (const char){},                        \
+		char *:        (char){},                              \
+		void *:        (char){}                               \
+	)                                                             \
+)
+
 void *memcpy (void *__restrict, const void *__restrict, size_t);
 void *memmove (void *, const void *, size_t);
 void *memset (void *, int, size_t);
@@ -55,6 +65,13 @@ size_t strlen (const char *);
 
 char *strerror (int);
 
+#if __STDC_VERSION__ >= 202311L
+# define strchr(s, ...)   ((__QCharof(s) *){ strchr(s, __VA_ARGS__)})
+# define strrchr(s, ...)  ((__QCharof(s) *){strrchr(s, __VA_ARGS__)})
+# define strpbrk(s, ...)  ((__QCharof(s) *){strpbrk(s, __VA_ARGS__)})
+# define strstr(s, ...)   ((__QCharof(s) *){ strstr(s, __VA_ARGS__)})
+#endif
+
 #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
 #include <strings.h>
 #endif
@@ -95,6 +112,10 @@ char *strchrnul(const char *, int);
 char *strcasestr(const char *, const char *);
 void *memrchr(const void *, int, size_t);
 void *mempcpy(void *, const void *, size_t);
+# if __STDC_VERSION__ >= 202311L
+#  define strchrnul(s, ...)   ((__QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
+#  define strcasestr(s, ...)  ((__QCharof(s) *){strcasestr(s, __VA_ARGS__)})
+# endif
 #endif
 
 #ifdef __cplusplus
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v2 0/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-25 13:51 [musl] [PATCH v1 0/1] include/string.h: Implement QChar wrappers standardized in C23 Alejandro Colomar
  2025-11-25 13:51 ` [musl] [PATCH v1 1/1] " Alejandro Colomar
@ 2025-11-25 21:49 ` Alejandro Colomar
  2025-11-25 21:49   ` [musl] [PATCH v2 1/1] " Alejandro Colomar
  2025-11-26 21:50 ` [musl] [PATCH v3 0/1] " Alejandro Colomar
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-25 21:49 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar

Hi!

In this v2 revision (see range-diff below), I've added the register
storage-class specifier to avoid accepting

	&strchr(s, c);

Tested:

	alx@devuan:~/tmp$ cat strchr.c 
	#include <stdio.h>
	#include <string.h>

	int
	main(int argc, const char *argv[argc + 1])
	{
		char *p;

		p = strchr(argv[0], 'u');
		puts(p);

		const char *cp;

		cp = strchr(argv[0], 'u');
		puts(cp);

		&strchr(argv[0], 'u');
	}
	alx@devuan:~/tmp$ gcc -Wall -Wextra strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:17:9: error: lvalue required as unary ‘&’ operand
	   17 |         &strchr(argv[0], 'u');
	      |         ^
	alx@devuan:~/tmp$ gcc -Wall -Wextra -I /opt/local/musl/libc/qchar/include/ strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:9:11: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
	    9 |         p = strchr(argv[0], 'u');
	      |           ^
	strchr.c:17:9: error: address of register compound literal requested
	   17 |         &strchr(argv[0], 'u');
	      |         ^


Have a lovely night!
Alex

Alejandro Colomar (1):
  include/string.h: Implement QChar wrappers standardized in C23

 include/string.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

Range-diff against v1:
1:  3cf2501c ! 1:  5cd6a281 include/string.h: Implement QChar wrappers standardized in C23
    @@ Metadata
      ## Commit message ##
         include/string.h: Implement QChar wrappers standardized in C23
     
    +    I used compound literals instead of casts because they're less
    +    dangerous.  They only allow conversions that would be legal implicitly.
    +
    +    The register storage-class specifier is used because compound literals
    +    are lvalues.  register makes sure one can't take the address of them.
    +    That is, it rejects:
    +
    +            &strchr(s, c);
    +
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## include/string.h ##
    @@ include/string.h: size_t strlen (const char *);
      char *strerror (int);
      
     +#if __STDC_VERSION__ >= 202311L
    -+# define strchr(s, ...)   ((__QCharof(s) *){ strchr(s, __VA_ARGS__)})
    -+# define strrchr(s, ...)  ((__QCharof(s) *){strrchr(s, __VA_ARGS__)})
    -+# define strpbrk(s, ...)  ((__QCharof(s) *){strpbrk(s, __VA_ARGS__)})
    -+# define strstr(s, ...)   ((__QCharof(s) *){ strstr(s, __VA_ARGS__)})
    ++# define strchr(s, ...)   ((register __QCharof(s) *){ strchr(s, __VA_ARGS__)})
    ++# define strrchr(s, ...)  ((register __QCharof(s) *){strrchr(s, __VA_ARGS__)})
    ++# define strpbrk(s, ...)  ((register __QCharof(s) *){strpbrk(s, __VA_ARGS__)})
    ++# define strstr(s, ...)   ((register __QCharof(s) *){ strstr(s, __VA_ARGS__)})
     +#endif
     +
      #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
    @@ include/string.h: char *strchrnul(const char *, int);
      void *memrchr(const void *, int, size_t);
      void *mempcpy(void *, const void *, size_t);
     +# if __STDC_VERSION__ >= 202311L
    -+#  define strchrnul(s, ...)   ((__QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
    -+#  define strcasestr(s, ...)  ((__QCharof(s) *){strcasestr(s, __VA_ARGS__)})
    ++#  define strchrnul(s, ...)   ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
    ++#  define strcasestr(s, ...)  ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
     +# endif
      #endif
      
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-25 21:49 ` [musl] [PATCH v2 0/1] " Alejandro Colomar
@ 2025-11-25 21:49   ` Alejandro Colomar
  2025-11-26  1:34     ` Rich Felker
  0 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-25 21:49 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar

I used compound literals instead of casts because they're less
dangerous.  They only allow conversions that would be legal implicitly.

The register storage-class specifier is used because compound literals
are lvalues.  register makes sure one can't take the address of them.
That is, it rejects:

	&strchr(s, c);

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/string.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/include/string.h b/include/string.h
index 83e2b946..c492698a 100644
--- a/include/string.h
+++ b/include/string.h
@@ -24,6 +24,16 @@ extern "C" {
 
 #include <bits/alltypes.h>
 
+#define __QCharof(s)  typeof                                            \
+(                                                                     \
+	_Generic(s,                                                   \
+		const char *:  (const char){},                        \
+		const void *:  (const char){},                        \
+		char *:        (char){},                              \
+		void *:        (char){}                               \
+	)                                                             \
+)
+
 void *memcpy (void *__restrict, const void *__restrict, size_t);
 void *memmove (void *, const void *, size_t);
 void *memset (void *, int, size_t);
@@ -55,6 +65,13 @@ size_t strlen (const char *);
 
 char *strerror (int);
 
+#if __STDC_VERSION__ >= 202311L
+# define strchr(s, ...)   ((register __QCharof(s) *){ strchr(s, __VA_ARGS__)})
+# define strrchr(s, ...)  ((register __QCharof(s) *){strrchr(s, __VA_ARGS__)})
+# define strpbrk(s, ...)  ((register __QCharof(s) *){strpbrk(s, __VA_ARGS__)})
+# define strstr(s, ...)   ((register __QCharof(s) *){ strstr(s, __VA_ARGS__)})
+#endif
+
 #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
 #include <strings.h>
 #endif
@@ -95,6 +112,10 @@ char *strchrnul(const char *, int);
 char *strcasestr(const char *, const char *);
 void *memrchr(const void *, int, size_t);
 void *mempcpy(void *, const void *, size_t);
+# if __STDC_VERSION__ >= 202311L
+#  define strchrnul(s, ...)   ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
+#  define strcasestr(s, ...)  ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
+# endif
 #endif
 
 #ifdef __cplusplus
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-25 21:49   ` [musl] [PATCH v2 1/1] " Alejandro Colomar
@ 2025-11-26  1:34     ` Rich Felker
  2025-11-26 12:22       ` Alejandro Colomar
  0 siblings, 1 reply; 22+ messages in thread
From: Rich Felker @ 2025-11-26  1:34 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: musl

On Tue, Nov 25, 2025 at 10:49:29PM +0100, Alejandro Colomar wrote:
> I used compound literals instead of casts because they're less
> dangerous.  They only allow conversions that would be legal implicitly.
> 
> The register storage-class specifier is used because compound literals
> are lvalues.  register makes sure one can't take the address of them.
> That is, it rejects:
> 
> 	&strchr(s, c);
> 
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> ---
>  include/string.h | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/include/string.h b/include/string.h
> index 83e2b946..c492698a 100644
> --- a/include/string.h
> +++ b/include/string.h
> @@ -24,6 +24,16 @@ extern "C" {
>  
>  #include <bits/alltypes.h>
>  
> +#define __QCharof(s)  typeof                                            \
> +(                                                                     \
> +	_Generic(s,                                                   \
> +		const char *:  (const char){},                        \
> +		const void *:  (const char){},                        \
> +		char *:        (char){},                              \
> +		void *:        (char){}                               \
> +	)                                                             \
> +)
> +
>  void *memcpy (void *__restrict, const void *__restrict, size_t);
>  void *memmove (void *, const void *, size_t);
>  void *memset (void *, int, size_t);
> @@ -55,6 +65,13 @@ size_t strlen (const char *);
>  
>  char *strerror (int);
>  
> +#if __STDC_VERSION__ >= 202311L
> +# define strchr(s, ...)   ((register __QCharof(s) *){ strchr(s, __VA_ARGS__)})
> +# define strrchr(s, ...)  ((register __QCharof(s) *){strrchr(s, __VA_ARGS__)})
> +# define strpbrk(s, ...)  ((register __QCharof(s) *){strpbrk(s, __VA_ARGS__)})
> +# define strstr(s, ...)   ((register __QCharof(s) *){ strstr(s, __VA_ARGS__)})
> +#endif
> +
>  #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
>  #include <strings.h>
>  #endif
> @@ -95,6 +112,10 @@ char *strchrnul(const char *, int);
>  char *strcasestr(const char *, const char *);
>  void *memrchr(const void *, int, size_t);
>  void *mempcpy(void *, const void *, size_t);
> +# if __STDC_VERSION__ >= 202311L
> +#  define strchrnul(s, ...)   ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
> +#  define strcasestr(s, ...)  ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
> +# endif
>  #endif
>  
>  #ifdef __cplusplus
> -- 
> 2.51.0

Could you explain a bit about your motivations for doing it this way?
Why are compound literals needed at all instead of just a value cast?

Also, can't __Qcharof just be defined as something like
typeof(1?(s):"") without any fancy _Generic machinery?

Rich

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-26  1:34     ` Rich Felker
@ 2025-11-26 12:22       ` Alejandro Colomar
  2025-11-26 14:17         ` Rich Felker
  0 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-26 12:22 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

[-- Attachment #1: Type: text/plain, Size: 4353 bytes --]

Hi Rich,

On Tue, Nov 25, 2025 at 08:34:39PM -0500, Rich Felker wrote:
> On Tue, Nov 25, 2025 at 10:49:29PM +0100, Alejandro Colomar wrote:
[...]
> > +# if __STDC_VERSION__ >= 202311L
> > +#  define strchrnul(s, ...)   ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
> > +#  define strcasestr(s, ...)  ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
> > +# endif
> >  #endif
> >  
> >  #ifdef __cplusplus
> > -- 
> > 2.51.0
> 
> Could you explain a bit about your motivations for doing it this way?
> Why are compound literals needed at all instead of just a value cast?

Yup.  I try to have 0 casts in my code.  They silence several classes of
diagnostics, and allow arbitrary conversions.

For example, they could silence a bug in the prototype of strchr(3) if
it accidentally returned an int*.  Admittedly, <string.h> is so stable
that it's not realistically going to have such bugs, so most of the
issues it prevents are theoretical.

In my own projects, I don't trust myself so much, so I perform all
return-value conversions through compound literals, which only allow
implicit conversions.

BTW, I've realized that there's another thing I need to fix in the
compound literals.  They should be const to prevent assignment to them.
That is, my current implementation allows

	strchr(s, c) = NULL;

See a test:

	alx@devuan:~/tmp$ cat strchr.c 
	#include <stdio.h>
	#include <string.h>

	int
	main(int argc, const char *argv[argc + 1])
	{
		char *p;

		p = strchr(argv[0], 'u');
		puts(p);

		const char *cp;

		cp = strchr(argv[0], 'u');
		puts(cp);

		&strchr(argv[0], 'u');

		strchr(argv[0], 'u') = NULL;
	}
	alx@devuan:~/tmp$ gcc strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:17:9: error: lvalue required as unary ‘&’ operand
	   17 |         &strchr(argv[0], 'u');
	      |         ^
	strchr.c:19:30: error: lvalue required as left operand of assignment
	   19 |         strchr(argv[0], 'u') = NULL;
	      |                              ^
	alx@devuan:~/tmp$ gcc -I /opt/local/musl/libc/qchar/include/ strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:9:11: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
	    9 |         p = strchr(argv[0], 'u');
	      |           ^
	strchr.c:17:9: error: address of register compound literal requested
	   17 |         &strchr(argv[0], 'u');
	      |         ^

I need to add a const qualifier to reject that:

	#define strchr(s, c)   ((register __QCharof(s) *const){ strchr(s, c)})

Alternatively, using a GNU extension, we can perform an lvalue
conversion of the compound literal, which solves the problems caused by
compound literals being lvalues:

	#define strchr(s, c)  ({(__QCharof(s) *){strchr(s, c)};})

This uses a statement expression ({...}), however.  Is that okay?
It depends on which features can be required in musl's headers.

However, if you prefer it with a cast, I can change it.  It's slightly
less safe, but could be okay given how stable <string.h> should be.

> Also, can't __Qcharof just be defined as something like
> typeof(1?(s):"") without any fancy _Generic machinery?

This wouldn't accept void*, and these functions should accept void*
arguments.

	alx@devuan:~/tmp$ cat typeof.c 
	#include <stdio.h>
	int
	main(void)
	{
		const char *cc;
		const void *cv;
		char *c;
		void *v;

		_Generic(typeof(1?cc:""), const char *: 0);
		_Generic(typeof(1?cv:""), const char *: 0);
		_Generic(typeof(1? c:""),       char *: 0);
		_Generic(typeof(1? v:""),       char *: 0);
	}
	alx@devuan:~/tmp$ gcc typeof.c 
	typeof.c: In function ‘main’:
	typeof.c:11:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
	   11 |         _Generic(typeof(1?cv:""), const char *: 0);
	      |                  ^~~~~~
	typeof.c:13:18: error: ‘_Generic’ selector of type ‘void *’ is not compatible with any association
	   13 |         _Generic(typeof(1? v:""),       char *: 0);
	      |                  ^~~~~~

I don't know if there might be a way to write it without _Generic(3),
but I haven't found it.


Have a lovely day!
Alex

> 
> Rich

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-26 12:22       ` Alejandro Colomar
@ 2025-11-26 14:17         ` Rich Felker
  2025-11-26 14:56           ` Alejandro Colomar
  0 siblings, 1 reply; 22+ messages in thread
From: Rich Felker @ 2025-11-26 14:17 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: musl

On Wed, Nov 26, 2025 at 01:22:17PM +0100, Alejandro Colomar wrote:
> Hi Rich,
> 
> On Tue, Nov 25, 2025 at 08:34:39PM -0500, Rich Felker wrote:
> > On Tue, Nov 25, 2025 at 10:49:29PM +0100, Alejandro Colomar wrote:
> [...]
> > > +# if __STDC_VERSION__ >= 202311L
> > > +#  define strchrnul(s, ...)   ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
> > > +#  define strcasestr(s, ...)  ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
> > > +# endif
> > >  #endif
> > >  
> > >  #ifdef __cplusplus
> > > -- 
> > > 2.51.0
> > 
> > Could you explain a bit about your motivations for doing it this way?
> > Why are compound literals needed at all instead of just a value cast?
> 
> Yup.  I try to have 0 casts in my code.  They silence several classes of
> diagnostics, and allow arbitrary conversions.
> 
> For example, they could silence a bug in the prototype of strchr(3) if
> it accidentally returned an int*.  Admittedly, <string.h> is so stable
> that it's not realistically going to have such bugs, so most of the
> issues it prevents are theoretical.

Yes, that is not an issue for libc.

> In my own projects, I don't trust myself so much, so I perform all
> return-value conversions through compound literals, which only allow
> implicit conversions.

Understandable, but it's not something we need to copy into musl.

> Alternatively, using a GNU extension, we can perform an lvalue
> conversion of the compound literal, which solves the problems caused by
> compound literals being lvalues:
> 
> 	#define strchr(s, c)  ({(__QCharof(s) *){strchr(s, c)};})
> 
> This uses a statement expression ({...}), however.  Is that okay?
> It depends on which features can be required in musl's headers.

No, we do not use statement-expressions.

> However, if you prefer it with a cast, I can change it.  It's slightly
> less safe, but could be okay given how stable <string.h> should be.

It's absolutely safe; you can't define unsafety of one thing on the
basis of making another disallowed change that's world-breakingly
unsafe already.

> > Also, can't __Qcharof just be defined as something like
> > typeof(1?(s):"") without any fancy _Generic machinery?
> 
> This wouldn't accept void*, and these functions should accept void*
> arguments.

I was thinking the ternary of char* and void* would produce char*, but
indeed I'm always wrong about that.

> I don't know if there might be a way to write it without _Generic(3),
> but I haven't found it.

Indeed, I can't think of any at the moment.

Rich

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-26 14:17         ` Rich Felker
@ 2025-11-26 14:56           ` Alejandro Colomar
  2025-11-26 15:02             ` Rich Felker
  0 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-26 14:56 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

[-- Attachment #1: Type: text/plain, Size: 5247 bytes --]

Hi Rich,

On Wed, Nov 26, 2025 at 09:17:11AM -0500, Rich Felker wrote:
> On Wed, Nov 26, 2025 at 01:22:17PM +0100, Alejandro Colomar wrote:
> > Hi Rich,
> > 
> > On Tue, Nov 25, 2025 at 08:34:39PM -0500, Rich Felker wrote:
> > > On Tue, Nov 25, 2025 at 10:49:29PM +0100, Alejandro Colomar wrote:
> > [...]
> > > > +# if __STDC_VERSION__ >= 202311L
> > > > +#  define strchrnul(s, ...)   ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
> > > > +#  define strcasestr(s, ...)  ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
> > > > +# endif
> > > >  #endif
> > > >  
> > > >  #ifdef __cplusplus
> > > > -- 
> > > > 2.51.0
> > > 
> > > Could you explain a bit about your motivations for doing it this way?
> > > Why are compound literals needed at all instead of just a value cast?
> > 
> > Yup.  I try to have 0 casts in my code.  They silence several classes of
> > diagnostics, and allow arbitrary conversions.
> > 
> > For example, they could silence a bug in the prototype of strchr(3) if
> > it accidentally returned an int*.  Admittedly, <string.h> is so stable
> > that it's not realistically going to have such bugs, so most of the
> > issues it prevents are theoretical.
> 
> Yes, that is not an issue for libc.
> 
> > In my own projects, I don't trust myself so much, so I perform all
> > return-value conversions through compound literals, which only allow
> > implicit conversions.
> 
> Understandable, but it's not something we need to copy into musl.
> 
[...]
> 
> > However, if you prefer it with a cast, I can change it.  It's slightly
> > less safe, but could be okay given how stable <string.h> should be.
> 
> It's absolutely safe; you can't define unsafety of one thing on the
> basis of making another disallowed change that's world-breakingly
> unsafe already.

Except that once these functions are wrapped through these macros, it
wouldn't break the world anymore, so a future typo in the return type of
strchr(3) could go unnoticed.  The only code that would break with such
an accidental change would be function pointers.  But how often are
function pointers to these function taken?  I've never seen such code.

Also, if new functions such as strprefix()/strpfx() and
strsuffix()/strsfx() are added (I'm working on that in the C Committee),
those would have less existing uses, which would mean a typo there would
almost certainly go unnoticed.

But anyway, I understand prefering a cast for the simplicity.

> > > Also, can't __Qcharof just be defined as something like
> > > typeof(1?(s):"") without any fancy _Generic machinery?
> > 
> > This wouldn't accept void*, and these functions should accept void*
> > arguments.
> 
> I was thinking the ternary of char* and void* would produce char*, but
> indeed I'm always wrong about that.

You weren't totally wrong.  There's special case where that's true: NULL

	alx@devuan:~/tmp$ cat typeof.c 
	#include <stdio.h>
	int
	main(void)
	{
		const char *cc;
		const void *cv;
		char *c;
		void *v;

		_Generic(typeof(1?cc:""), const char *: 0);
		_Generic(typeof(1?cv:""), const char *: 0);
		_Generic(typeof(1? c:""),       char *: 0);
		_Generic(typeof(1? v:""),       char *: 0);

		_Generic(typeof(1?(void *)      0:""), const char *: 0);
		_Generic(typeof(1?(const void *)0:""),       char *: 0);
	}
	alx@devuan:~/tmp$ gcc typeof.c 
	typeof.c: In function ‘main’:
	typeof.c:11:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
	   11 |         _Generic(typeof(1?cv:""), const char *: 0);
	      |                  ^~~~~~
	typeof.c:13:18: error: ‘_Generic’ selector of type ‘void *’ is not compatible with any association
	   13 |         _Generic(typeof(1? v:""),       char *: 0);
	      |                  ^~~~~~
	typeof.c:15:18: error: ‘_Generic’ selector of type ‘char *’ is not compatible with any association
	   15 |         _Generic(typeof(1?(void *)      0:""), const char *: 0);
	      |                  ^~~~~~
	typeof.c:16:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
	   16 |         _Generic(typeof(1?(const void *)0:""),       char *: 0);
	      |                  ^~~~~~

As you can see, typeof(1?(void*)0:"") produces a char*.  Any other void*
doesn't.  This means that the type of NULL is a magic type different
from void*, but which is compatible with void*.  This is slightly
different from nullptr_t in that _Generic(3) is able to distinguish
nullptr_t form void*, but not the type of NULL from void*, but it's
pretty similar.

> > I don't know if there might be a way to write it without _Generic(3),
> > but I haven't found it.
> 
> Indeed, I can't think of any at the moment.

Okay, I'll send a revision using a cast.  If you ever change your mind
and want to use a compound literal it should use 'register' and 'const':

	#define strchr(s,c) ((register __QCharof(s) *const){strchr(s,c)})

Where register prevents
	&strchr(s,c);

And const prevents
	strchr(s,c) = NULL;


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-26 14:56           ` Alejandro Colomar
@ 2025-11-26 15:02             ` Rich Felker
  2025-11-26 15:36               ` Alejandro Colomar
  0 siblings, 1 reply; 22+ messages in thread
From: Rich Felker @ 2025-11-26 15:02 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: musl

On Wed, Nov 26, 2025 at 03:56:21PM +0100, Alejandro Colomar wrote:
> Hi Rich,
> 
> On Wed, Nov 26, 2025 at 09:17:11AM -0500, Rich Felker wrote:
> > On Wed, Nov 26, 2025 at 01:22:17PM +0100, Alejandro Colomar wrote:
> > > Hi Rich,
> > > 
> > > On Tue, Nov 25, 2025 at 08:34:39PM -0500, Rich Felker wrote:
> > > > On Tue, Nov 25, 2025 at 10:49:29PM +0100, Alejandro Colomar wrote:
> > > [...]
> > > > > +# if __STDC_VERSION__ >= 202311L
> > > > > +#  define strchrnul(s, ...)   ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
> > > > > +#  define strcasestr(s, ...)  ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
> > > > > +# endif
> > > > >  #endif
> > > > >  
> > > > >  #ifdef __cplusplus
> > > > > -- 
> > > > > 2.51.0
> > > > 
> > > > Could you explain a bit about your motivations for doing it this way?
> > > > Why are compound literals needed at all instead of just a value cast?
> > > 
> > > Yup.  I try to have 0 casts in my code.  They silence several classes of
> > > diagnostics, and allow arbitrary conversions.
> > > 
> > > For example, they could silence a bug in the prototype of strchr(3) if
> > > it accidentally returned an int*.  Admittedly, <string.h> is so stable
> > > that it's not realistically going to have such bugs, so most of the
> > > issues it prevents are theoretical.
> > 
> > Yes, that is not an issue for libc.
> > 
> > > In my own projects, I don't trust myself so much, so I perform all
> > > return-value conversions through compound literals, which only allow
> > > implicit conversions.
> > 
> > Understandable, but it's not something we need to copy into musl.
> > 
> [...]
> > 
> > > However, if you prefer it with a cast, I can change it.  It's slightly
> > > less safe, but could be okay given how stable <string.h> should be.
> > 
> > It's absolutely safe; you can't define unsafety of one thing on the
> > basis of making another disallowed change that's world-breakingly
> > unsafe already.
> 
> Except that once these functions are wrapped through these macros, it
> wouldn't break the world anymore, so a future typo in the return type of
> strchr(3) could go unnoticed.  The only code that would break with such
> an accidental change would be function pointers.  But how often are
> function pointers to these function taken?  I've never seen such code.

It doesn't have to be functionally breaking common code in the wild to
be breaking the *contract*. You'd not allowed to change them because
the specification fixes the signatures in stone.

> > > > Also, can't __Qcharof just be defined as something like
> > > > typeof(1?(s):"") without any fancy _Generic machinery?
> > > 
> > > This wouldn't accept void*, and these functions should accept void*
> > > arguments.
> > 
> > I was thinking the ternary of char* and void* would produce char*, but
> > indeed I'm always wrong about that.
> 
> You weren't totally wrong.  There's special case where that's true: NULL
> 
> 	alx@devuan:~/tmp$ cat typeof.c 
> 	#include <stdio.h>
> 	int
> 	main(void)
> 	{
> 		const char *cc;
> 		const void *cv;
> 		char *c;
> 		void *v;
> 
> 		_Generic(typeof(1?cc:""), const char *: 0);
> 		_Generic(typeof(1?cv:""), const char *: 0);
> 		_Generic(typeof(1? c:""),       char *: 0);
> 		_Generic(typeof(1? v:""),       char *: 0);
> 
> 		_Generic(typeof(1?(void *)      0:""), const char *: 0);
> 		_Generic(typeof(1?(const void *)0:""),       char *: 0);
> 	}
> 	alx@devuan:~/tmp$ gcc typeof.c 
> 	typeof.c: In function ‘main’:
> 	typeof.c:11:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> 	   11 |         _Generic(typeof(1?cv:""), const char *: 0);
> 	      |                  ^~~~~~
> 	typeof.c:13:18: error: ‘_Generic’ selector of type ‘void *’ is not compatible with any association
> 	   13 |         _Generic(typeof(1? v:""),       char *: 0);
> 	      |                  ^~~~~~
> 	typeof.c:15:18: error: ‘_Generic’ selector of type ‘char *’ is not compatible with any association
> 	   15 |         _Generic(typeof(1?(void *)      0:""), const char *: 0);
> 	      |                  ^~~~~~
> 	typeof.c:16:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> 	   16 |         _Generic(typeof(1?(const void *)0:""),       char *: 0);
> 	      |                  ^~~~~~
> 
> As you can see, typeof(1?(void*)0:"") produces a char*.  Any other void*
> doesn't.  This means that the type of NULL is a magic type different
> from void*, but which is compatible with void*.  This is slightly
> different from nullptr_t in that _Generic(3) is able to distinguish
> nullptr_t form void*, but not the type of NULL from void*, but it's
> pretty similar.

typeof(1?"":(typeof(s))0) maybe? I think it works!

Rich

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-26 15:02             ` Rich Felker
@ 2025-11-26 15:36               ` Alejandro Colomar
  2025-11-26 21:18                 ` Rich Felker
  0 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-26 15:36 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

[-- Attachment #1: Type: text/plain, Size: 5469 bytes --]

Hi Rich,

On Wed, Nov 26, 2025 at 10:02:22AM -0500, Rich Felker wrote:
> On Wed, Nov 26, 2025 at 03:56:21PM +0100, Alejandro Colomar wrote:
[...]
> It doesn't have to be functionally breaking common code in the wild to
> be breaking the *contract*. You'd not allowed to change them because
> the specification fixes the signatures in stone.

Agree.  Given how few functions use QChar in the standard library, and
how stable the standard library is, typos would be unlikely and would be
obvious enough that they wouldn't pass any review.

> > > > > Also, can't __Qcharof just be defined as something like
> > > > > typeof(1?(s):"") without any fancy _Generic machinery?
> > > > 
> > > > This wouldn't accept void*, and these functions should accept void*
> > > > arguments.
> > > 
> > > I was thinking the ternary of char* and void* would produce char*, but
> > > indeed I'm always wrong about that.
> > 
> > You weren't totally wrong.  There's special case where that's true: NULL
> > 
> > 	alx@devuan:~/tmp$ cat typeof.c 
> > 	#include <stdio.h>
> > 	int
> > 	main(void)
> > 	{
> > 		const char *cc;
> > 		const void *cv;
> > 		char *c;
> > 		void *v;
> > 
> > 		_Generic(typeof(1?cc:""), const char *: 0);
> > 		_Generic(typeof(1?cv:""), const char *: 0);
> > 		_Generic(typeof(1? c:""),       char *: 0);
> > 		_Generic(typeof(1? v:""),       char *: 0);
> > 
> > 		_Generic(typeof(1?(void *)      0:""), const char *: 0);
> > 		_Generic(typeof(1?(const void *)0:""),       char *: 0);
> > 	}
> > 	alx@devuan:~/tmp$ gcc typeof.c 
> > 	typeof.c: In function ‘main’:
> > 	typeof.c:11:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> > 	   11 |         _Generic(typeof(1?cv:""), const char *: 0);
> > 	      |                  ^~~~~~
> > 	typeof.c:13:18: error: ‘_Generic’ selector of type ‘void *’ is not compatible with any association
> > 	   13 |         _Generic(typeof(1? v:""),       char *: 0);
> > 	      |                  ^~~~~~
> > 	typeof.c:15:18: error: ‘_Generic’ selector of type ‘char *’ is not compatible with any association
> > 	   15 |         _Generic(typeof(1?(void *)      0:""), const char *: 0);
> > 	      |                  ^~~~~~
> > 	typeof.c:16:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> > 	   16 |         _Generic(typeof(1?(const void *)0:""),       char *: 0);
> > 	      |                  ^~~~~~
> > 
> > As you can see, typeof(1?(void*)0:"") produces a char*.  Any other void*
> > doesn't.  This means that the type of NULL is a magic type different
> > from void*, but which is compatible with void*.  This is slightly
> > different from nullptr_t in that _Generic(3) is able to distinguish
> > nullptr_t form void*, but not the type of NULL from void*, but it's
> > pretty similar.
> 
> typeof(1?"":(typeof(s))0) maybe? I think it works!

Almost!  It's closer.  :)

	alx@devuan:~/tmp$ cat typeof.c 
	#include <stdio.h>

	#if defined TRY1
	# define QCharptrof(s)  typeof(1?s:"")

	#elif defined TRY2
	# define QCharptrof(s)  typeof(1?"":(typeof(s))0)

	#else
	# define QCharptrof(s)  typeof(QCharof(s) *)
	# define QCharof(s)     typeof                                \
	(                                                             \
		_Generic(s,                                           \
			const char *:  (const char){},                \
			const void *:  (const char){},                \
			char *:        (char){},                      \
			void *:        (char){}                       \
		)                                                     \
	)
	#endif

	int
	main(void)
	{
		const char *cc;
		const void *cv;
		char *c;
		void *v;

		_Generic(QCharptrof(  cc), const char *: 0);
		_Generic(QCharptrof(  cv), const char *: 0);
		_Generic(QCharptrof(   c),       char *: 0);
		_Generic(QCharptrof(   v),       char *: 0);
		_Generic(QCharptrof(NULL),       char *: 0);
	}

	alx@devuan:~/tmp$ gcc typeof.c 
	alx@devuan:~/tmp$ gcc -D TRY1 typeof.c 
	typeof.c: In function ‘main’:
	typeof.c:4:25: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
	    4 | # define QCharptrof(s)  typeof(1?s:"")
	      |                         ^~~~~~
	typeof.c:31:18: note: in expansion of macro ‘QCharptrof’
	   31 |         _Generic(QCharptrof(  cv), const char *: 0);
	      |                  ^~~~~~~~~~
	typeof.c:4:25: error: ‘_Generic’ selector of type ‘void *’ is not compatible with any association
	    4 | # define QCharptrof(s)  typeof(1?s:"")
	      |                         ^~~~~~
	typeof.c:33:18: note: in expansion of macro ‘QCharptrof’
	   33 |         _Generic(QCharptrof(   v),       char *: 0);
	      |                  ^~~~~~~~~~
	alx@devuan:~/tmp$ gcc -D TRY2 typeof.c 
	typeof.c: In function ‘main’:
	typeof.c:7:25: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
	    7 | # define QCharptrof(s)  typeof(1?"":(typeof(s))0)
	      |                         ^~~~~~
	typeof.c:31:18: note: in expansion of macro ‘QCharptrof’
	   31 |         _Generic(QCharptrof(  cv), const char *: 0);
	      |                  ^~~~~~~~~~


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-26 15:36               ` Alejandro Colomar
@ 2025-11-26 21:18                 ` Rich Felker
  2025-11-27 15:01                   ` Markus Wichmann
  0 siblings, 1 reply; 22+ messages in thread
From: Rich Felker @ 2025-11-26 21:18 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: musl

On Wed, Nov 26, 2025 at 04:36:38PM +0100, Alejandro Colomar wrote:
> Hi Rich,
> 
> On Wed, Nov 26, 2025 at 10:02:22AM -0500, Rich Felker wrote:
> > On Wed, Nov 26, 2025 at 03:56:21PM +0100, Alejandro Colomar wrote:
> [...]
> > It doesn't have to be functionally breaking common code in the wild to
> > be breaking the *contract*. You'd not allowed to change them because
> > the specification fixes the signatures in stone.
> 
> Agree.  Given how few functions use QChar in the standard library, and
> how stable the standard library is, typos would be unlikely and would be
> obvious enough that they wouldn't pass any review.
> 
> > > > > > Also, can't __Qcharof just be defined as something like
> > > > > > typeof(1?(s):"") without any fancy _Generic machinery?
> > > > > 
> > > > > This wouldn't accept void*, and these functions should accept void*
> > > > > arguments.
> > > > 
> > > > I was thinking the ternary of char* and void* would produce char*, but
> > > > indeed I'm always wrong about that.
> > > 
> > > You weren't totally wrong.  There's special case where that's true: NULL
> > > 
> > > 	alx@devuan:~/tmp$ cat typeof.c 
> > > 	#include <stdio.h>
> > > 	int
> > > 	main(void)
> > > 	{
> > > 		const char *cc;
> > > 		const void *cv;
> > > 		char *c;
> > > 		void *v;
> > > 
> > > 		_Generic(typeof(1?cc:""), const char *: 0);
> > > 		_Generic(typeof(1?cv:""), const char *: 0);
> > > 		_Generic(typeof(1? c:""),       char *: 0);
> > > 		_Generic(typeof(1? v:""),       char *: 0);
> > > 
> > > 		_Generic(typeof(1?(void *)      0:""), const char *: 0);
> > > 		_Generic(typeof(1?(const void *)0:""),       char *: 0);
> > > 	}
> > > 	alx@devuan:~/tmp$ gcc typeof.c 
> > > 	typeof.c: In function ‘main’:
> > > 	typeof.c:11:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> > > 	   11 |         _Generic(typeof(1?cv:""), const char *: 0);
> > > 	      |                  ^~~~~~
> > > 	typeof.c:13:18: error: ‘_Generic’ selector of type ‘void *’ is not compatible with any association
> > > 	   13 |         _Generic(typeof(1? v:""),       char *: 0);
> > > 	      |                  ^~~~~~
> > > 	typeof.c:15:18: error: ‘_Generic’ selector of type ‘char *’ is not compatible with any association
> > > 	   15 |         _Generic(typeof(1?(void *)      0:""), const char *: 0);
> > > 	      |                  ^~~~~~
> > > 	typeof.c:16:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> > > 	   16 |         _Generic(typeof(1?(const void *)0:""),       char *: 0);
> > > 	      |                  ^~~~~~
> > > 
> > > As you can see, typeof(1?(void*)0:"") produces a char*.  Any other void*
> > > doesn't.  This means that the type of NULL is a magic type different
> > > from void*, but which is compatible with void*.  This is slightly
> > > different from nullptr_t in that _Generic(3) is able to distinguish
> > > nullptr_t form void*, but not the type of NULL from void*, but it's
> > > pretty similar.
> > 
> > typeof(1?"":(typeof(s))0) maybe? I think it works!
> 
> Almost!  It's closer.  :)
> 
> 	alx@devuan:~/tmp$ cat typeof.c 
> 	#include <stdio.h>
> 
> 	#if defined TRY1
> 	# define QCharptrof(s)  typeof(1?s:"")
> 
> 	#elif defined TRY2
> 	# define QCharptrof(s)  typeof(1?"":(typeof(s))0)
> 
> 	#else
> 	# define QCharptrof(s)  typeof(QCharof(s) *)
> 	# define QCharof(s)     typeof                                \
> 	(                                                             \
> 		_Generic(s,                                           \
> 			const char *:  (const char){},                \
> 			const void *:  (const char){},                \
> 			char *:        (char){},                      \
> 			void *:        (char){}                       \
> 		)                                                     \
> 	)
> 	#endif
> 
> 	int
> 	main(void)
> 	{
> 		const char *cc;
> 		const void *cv;
> 		char *c;
> 		void *v;
> 
> 		_Generic(QCharptrof(  cc), const char *: 0);
> 		_Generic(QCharptrof(  cv), const char *: 0);
> 		_Generic(QCharptrof(   c),       char *: 0);
> 		_Generic(QCharptrof(   v),       char *: 0);
> 		_Generic(QCharptrof(NULL),       char *: 0);
> 	}
> 
> 	alx@devuan:~/tmp$ gcc typeof.c 
> 	alx@devuan:~/tmp$ gcc -D TRY1 typeof.c 
> 	typeof.c: In function ‘main’:
> 	typeof.c:4:25: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> 	    4 | # define QCharptrof(s)  typeof(1?s:"")
> 	      |                         ^~~~~~
> 	typeof.c:31:18: note: in expansion of macro ‘QCharptrof’
> 	   31 |         _Generic(QCharptrof(  cv), const char *: 0);
> 	      |                  ^~~~~~~~~~
> 	typeof.c:4:25: error: ‘_Generic’ selector of type ‘void *’ is not compatible with any association
> 	    4 | # define QCharptrof(s)  typeof(1?s:"")
> 	      |                         ^~~~~~
> 	typeof.c:33:18: note: in expansion of macro ‘QCharptrof’
> 	   33 |         _Generic(QCharptrof(   v),       char *: 0);
> 	      |                  ^~~~~~~~~~
> 	alx@devuan:~/tmp$ gcc -D TRY2 typeof.c 
> 	typeof.c: In function ‘main’:
> 	typeof.c:7:25: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> 	    7 | # define QCharptrof(s)  typeof(1?"":(typeof(s))0)
> 	      |                         ^~~~~~
> 	typeof.c:31:18: note: in expansion of macro ‘QCharptrof’
> 	   31 |         _Generic(QCharptrof(  cv), const char *: 0);
> 	      |                  ^~~~~~~~~~

Hmm, maybe it doesn't work. It looks like only exactly (void *)0 is a
null pointer constant. A cast to a qualified void pointer type isn't
one. I'm not sure if it's fixable.

Rich

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v3 0/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-25 13:51 [musl] [PATCH v1 0/1] include/string.h: Implement QChar wrappers standardized in C23 Alejandro Colomar
  2025-11-25 13:51 ` [musl] [PATCH v1 1/1] " Alejandro Colomar
  2025-11-25 21:49 ` [musl] [PATCH v2 0/1] " Alejandro Colomar
@ 2025-11-26 21:50 ` Alejandro Colomar
  2025-11-26 21:50   ` [musl] [PATCH v3 1/1] " Alejandro Colomar
  2025-11-27 16:27 ` [musl] [PATCH v4 0/1] include/string.h: Implement QChar and QVoid " Alejandro Colomar
  2025-12-01 17:53 ` [musl] [PATCH v5 0/1] " Alejandro Colomar
  4 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-26 21:50 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar

Hi!

In this v2 revision (see range-diff below), I've changed to use a cast,
as suggested by Rich.  __QCharof() remains implemented with _Generic(),
which is the only implementation that we know to work correctly.

Tested:

	alx@devuan:~/tmp$ cat strchr.c 
	#include <stdio.h>
	#include <string.h>

	int
	main(int argc, const char *argv[argc + 1])
	{
		char *p;


		p = strchr(argv[0], 'u');
		puts(p);

		const char *cp;

		cp = strchr(argv[0], 'u');
		puts(cp);

		&strchr(argv[0], 'u');

		strchr(argv[0], 'u') = NULL;
	}
	alx@devuan:~/tmp$ gcc strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:18:9: error: lvalue required as unary ‘&’ operand
	   18 |         &strchr(argv[0], 'u');
	      |         ^
	strchr.c:20:30: error: lvalue required as left operand of assignment
	   20 |         strchr(argv[0], 'u') = NULL;
	      |                              ^
	alx@devuan:~/tmp$ gcc -I /opt/local/musl/libc/qchar/include/ strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:10:11: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
	   10 |         p = strchr(argv[0], 'u');
	      |           ^
	strchr.c:18:9: error: lvalue required as unary ‘&’ operand
	   18 |         &strchr(argv[0], 'u');
	      |         ^
	strchr.c:20:30: error: lvalue required as left operand of assignment
	   20 |         strchr(argv[0], 'u') = NULL;
	      |                              ^


Have a lovely night!
Alex


Alejandro Colomar (1):
  include/string.h: Implement QChar wrappers standardized in C23

 include/string.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

Range-diff against v2:
1:  5cd6a281 ! 1:  c47e9570 include/string.h: Implement QChar wrappers standardized in C23
    @@ Metadata
      ## Commit message ##
         include/string.h: Implement QChar wrappers standardized in C23
     
    -    I used compound literals instead of casts because they're less
    -    dangerous.  They only allow conversions that would be legal implicitly.
    -
    -    The register storage-class specifier is used because compound literals
    -    are lvalues.  register makes sure one can't take the address of them.
    -    That is, it rejects:
    -
    -            &strchr(s, c);
    -
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## include/string.h ##
    @@ include/string.h: size_t strlen (const char *);
      char *strerror (int);
      
     +#if __STDC_VERSION__ >= 202311L
    -+# define strchr(s, ...)   ((register __QCharof(s) *){ strchr(s, __VA_ARGS__)})
    -+# define strrchr(s, ...)  ((register __QCharof(s) *){strrchr(s, __VA_ARGS__)})
    -+# define strpbrk(s, ...)  ((register __QCharof(s) *){strpbrk(s, __VA_ARGS__)})
    -+# define strstr(s, ...)   ((register __QCharof(s) *){ strstr(s, __VA_ARGS__)})
    ++# define strchr(s, chr)    ((__QCharof(s) *) strchr(s, chr))
    ++# define strrchr(s, chr)   ((__QCharof(s) *) strrchr(s, chr))
    ++# define strpbrk(s, chrs)  ((__QCharof(s) *) strpbrk(s, chrs))
    ++# define strstr(s, str)    ((__QCharof(s) *) strstr(s, str))
     +#endif
     +
      #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
    @@ include/string.h: char *strchrnul(const char *, int);
      void *memrchr(const void *, int, size_t);
      void *mempcpy(void *, const void *, size_t);
     +# if __STDC_VERSION__ >= 202311L
    -+#  define strchrnul(s, ...)   ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
    -+#  define strcasestr(s, ...)  ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
    ++#  define strchrnul(s, chr)   ((__QCharof(s) *) strchrnul(s, chr))
    ++#  define strcasestr(s, str)  ((__QCharof(s) *) strcasestr(s, str))
     +# endif
      #endif
      
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v3 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-26 21:50 ` [musl] [PATCH v3 0/1] " Alejandro Colomar
@ 2025-11-26 21:50   ` Alejandro Colomar
  0 siblings, 0 replies; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-26 21:50 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/string.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/include/string.h b/include/string.h
index 83e2b946..e8b91e25 100644
--- a/include/string.h
+++ b/include/string.h
@@ -24,6 +24,16 @@ extern "C" {
 
 #include <bits/alltypes.h>
 
+#define __QCharof(s)  typeof                                            \
+(                                                                     \
+	_Generic(s,                                                   \
+		const char *:  (const char){},                        \
+		const void *:  (const char){},                        \
+		char *:        (char){},                              \
+		void *:        (char){}                               \
+	)                                                             \
+)
+
 void *memcpy (void *__restrict, const void *__restrict, size_t);
 void *memmove (void *, const void *, size_t);
 void *memset (void *, int, size_t);
@@ -55,6 +65,13 @@ size_t strlen (const char *);
 
 char *strerror (int);
 
+#if __STDC_VERSION__ >= 202311L
+# define strchr(s, chr)    ((__QCharof(s) *) strchr(s, chr))
+# define strrchr(s, chr)   ((__QCharof(s) *) strrchr(s, chr))
+# define strpbrk(s, chrs)  ((__QCharof(s) *) strpbrk(s, chrs))
+# define strstr(s, str)    ((__QCharof(s) *) strstr(s, str))
+#endif
+
 #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
 #include <strings.h>
 #endif
@@ -95,6 +112,10 @@ char *strchrnul(const char *, int);
 char *strcasestr(const char *, const char *);
 void *memrchr(const void *, int, size_t);
 void *mempcpy(void *, const void *, size_t);
+# if __STDC_VERSION__ >= 202311L
+#  define strchrnul(s, chr)   ((__QCharof(s) *) strchrnul(s, chr))
+#  define strcasestr(s, str)  ((__QCharof(s) *) strcasestr(s, str))
+# endif
 #endif
 
 #ifdef __cplusplus
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-26 21:18                 ` Rich Felker
@ 2025-11-27 15:01                   ` Markus Wichmann
  2025-11-27 15:59                     ` Alejandro Colomar
  2025-11-27 17:05                     ` Rich Felker
  0 siblings, 2 replies; 22+ messages in thread
From: Markus Wichmann @ 2025-11-27 15:01 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar

Am Wed, Nov 26, 2025 at 04:18:25PM -0500 schrieb Rich Felker:
> Hmm, maybe it doesn't work. It looks like only exactly (void *)0 is a
> null pointer constant. A cast to a qualified void pointer type isn't
> one. I'm not sure if it's fixable.
> 
> Rich

There really is no expression that turns void* into char*, or at least
not that I could find. But it is possible to simplify the generic
expression Alejandro is using:

#define _QChar(s) typeof(_Generic(1?(s):(void *)"", void *: (char *)0, const void *: (const char *)0))
#define strstr(s, c) ((_QChar(s))strstr(s, c))

Feel free to reformat as you like.

The controlling expression of the generic is compatible with all pointer
types, and always becomes a void* or const void*. This means it already
has the correct type for the mem* functions and bsearch(), once you
start looking at those. For the str* functions, the only way I see to
get from those to char* is the generic expression.

The idea is to use an expression to query the const qualifier only.
According to the rules for conditional operators, I will get an equally
qualified void pointer only if the third expression is a non-null void
pointer (I would get the original type of s if it were a null pointer
constant). The simplest non-null pointer I can find is a string
literal.

I think it is better to let the const correctness macro only handle the
return value, because it leads to better error messages. With
Alejandro's current attempt, something like

int *p;
strstr(p, "");

will lead to an error message about no case existing for int* in the
generic expression, whereas my solution will leave the original error
message the compiler would generate from initializing a const char *
from an int * in place.

Ciao,
Markus

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-27 15:01                   ` Markus Wichmann
@ 2025-11-27 15:59                     ` Alejandro Colomar
  2025-11-27 17:05                     ` Rich Felker
  1 sibling, 0 replies; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-27 15:59 UTC (permalink / raw)
  To: Markus Wichmann; +Cc: musl

[-- Attachment #1: Type: text/plain, Size: 3379 bytes --]

Hi Markus,

On Thu, Nov 27, 2025 at 04:01:56PM +0100, Markus Wichmann wrote:
> Am Wed, Nov 26, 2025 at 04:18:25PM -0500 schrieb Rich Felker:
> > Hmm, maybe it doesn't work. It looks like only exactly (void *)0 is a
> > null pointer constant. A cast to a qualified void pointer type isn't
> > one. I'm not sure if it's fixable.
> > 
> > Rich
> 
> There really is no expression that turns void* into char*, or at least
> not that I could find. But it is possible to simplify the generic
> expression Alejandro is using:
> 
> #define _QChar(s) typeof(_Generic(1?(s):(void *)"", void *: (char *)0, const void *: (const char *)0))
> #define strstr(s, c) ((_QChar(s))strstr(s, c))

Interesting.  I think this is the key to implement __QVoid().  I was
scratching my head, but couldn't come up with a way.  I've split your
macro into two:

	alx@devuan:~/tmp$ cat typeof.c 
	#include <stddef.h>

	#define QVoidptrof(p)  typeof(1?(p):(void *)"")
	#define QCharptrof(s)  typeof(_Generic(QVoidptrof(s), void *: (char *)0, const void *: (const char *)0))

	int
	main(void)
	{
		const char *cc;
		const void *cv;
		const int  *ci;
		char *c;
		void *v;
		int  *i;

		_Generic(QCharptrof(  cc), const char *: 0);
		_Generic(QCharptrof(  cv), const char *: 0);
		_Generic(QCharptrof(   c),       char *: 0);
		_Generic(QCharptrof(   v),       char *: 0);
		_Generic(QCharptrof(NULL),       char *: 0);

		_Generic(QVoidptrof(  cc), const void *: 0);
		_Generic(QVoidptrof(  cv), const void *: 0);
		_Generic(QVoidptrof(  ci), const void *: 0);
		_Generic(QVoidptrof(   c),       void *: 0);
		_Generic(QVoidptrof(   v),       void *: 0);
		_Generic(QVoidptrof(   i),       void *: 0);
		_Generic(QVoidptrof(NULL),       void *: 0);
	}

	alx@devuan:~/tmp$ gcc typeof.c 
	alx@devuan:~/tmp$ 

I'll send a revision of the patch using this to also wrap the mem*()
functions.  Thanks!  I'll add a Co-authored-by tag, if you agree.

> Feel free to reformat as you like.
> 
> The controlling expression of the generic is compatible with all pointer
> types, and always becomes a void* or const void*. This means it already
> has the correct type for the mem* functions and bsearch(), once you
> start looking at those. For the str* functions, the only way I see to
> get from those to char* is the generic expression.

Yup, thanks!

> The idea is to use an expression to query the const qualifier only.
> According to the rules for conditional operators, I will get an equally
> qualified void pointer only if the third expression is a non-null void
> pointer (I would get the original type of s if it were a null pointer
> constant). The simplest non-null pointer I can find is a string
> literal.
> 
> I think it is better to let the const correctness macro only handle the
> return value, because it leads to better error messages. With
> Alejandro's current attempt, something like
> 
> int *p;
> strstr(p, "");
> 
> will lead to an error message about no case existing for int* in the
> generic expression, whereas my solution will leave the original error
> message the compiler would generate from initializing a const char *
> from an int * in place.

Sounds good.  Thanks!


Have a lovely day!
Alex

> 
> Ciao,
> Markus

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v4 0/1] include/string.h: Implement QChar and QVoid wrappers standardized in C23
  2025-11-25 13:51 [musl] [PATCH v1 0/1] include/string.h: Implement QChar wrappers standardized in C23 Alejandro Colomar
                   ` (2 preceding siblings ...)
  2025-11-26 21:50 ` [musl] [PATCH v3 0/1] " Alejandro Colomar
@ 2025-11-27 16:27 ` Alejandro Colomar
  2025-11-27 16:27   ` [musl] [PATCH v4 1/1] " Alejandro Colomar
  2025-12-01 17:53 ` [musl] [PATCH v5 0/1] " Alejandro Colomar
  4 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-27 16:27 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar, Markus Wichmann

Hi!

In v4, I've also added the QVoid wrappers, thanks to Markus's
suggestion.  See the range-diff below.

Tested:

	alx@devuan:~/tmp$ cat strchr.c 
	#include <stdio.h>
	#include <string.h>

	int
	main(int argc, const char *argv[argc + 1])
	{
		char *p;


		p = strchr(argv[0], 'u');
		puts(p);

		const char *cp;

		cp = strchr(argv[0], 'u');
		puts(cp);

		&strchr(argv[0], 'u');

		strchr(argv[0], 'u') = NULL;

		int *i;

		i = strchr(i, 'u');
	}
	alx@devuan:~/tmp$ gcc -I /opt/local/musl/libc/qchar/include/ strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:10:11: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
	   10 |         p = strchr(argv[0], 'u');
	      |           ^
	strchr.c:18:9: error: lvalue required as unary ‘&’ operand
	   18 |         &strchr(argv[0], 'u');
	      |         ^
	strchr.c:20:30: error: lvalue required as left operand of assignment
	   20 |         strchr(argv[0], 'u') = NULL;
	      |                              ^
	In file included from strchr.c:2:
	strchr.c:24:20: error: passing argument 1 of ‘strchr’ from incompatible pointer type [-Wincompatible-pointer-types]
	   24 |         i = strchr(i, 'u');
	      |                    ^
	      |                    |
	      |                    int *
	/opt/local/musl/libc/qchar/include/string.h:69:54: note: in definition of macro ‘strchr’
	   69 | # define strchr(s, chr)    ((__QCharptrof(s)) strchr(s, chr))
	      |                                                      ^
	/opt/local/musl/libc/qchar/include/string.h:54:15: note: expected ‘const char *’ but argument is of type ‘int *’
	   54 | char *strchr (const char *, int);
	      |               ^~~~~~~~~~~~
	strchr.c:24:11: error: assignment to ‘int *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types]
	   24 |         i = strchr(i, 'u');
	      |           ^



Have a lovely day!
Alex

Alejandro Colomar (1):
  include/string.h: Implement QChar and QVoid wrappers standardized in
    C23

 include/string.h | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

Range-diff against v3:
1:  c47e9570 ! 1:  b891d212 include/string.h: Implement QChar wrappers standardized in C23
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    include/string.h: Implement QChar wrappers standardized in C23
    +    include/string.h: Implement QChar and QVoid wrappers standardized in C23
     
    +    Co-authored-by: Markus Wichmann <nullplan@gmx.net>
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## include/string.h ##
    @@ include/string.h: extern "C" {
      
      #include <bits/alltypes.h>
      
    -+#define __QCharof(s)  typeof                                            \
    ++#define __QVoidptrof(p)  typeof(1?(p):(void*)"")
    ++#define __QCharptrof(s)  typeof                                       \
     +(                                                                     \
    -+	_Generic(s,                                                   \
   -+		const char *:  (const char){},                        \
    -+		const void *:  (const char){},                        \
    -+		char *:        (char){},                              \
    -+		void *:        (char){}                               \
    ++	_Generic(__QVoidptrof(s),                                     \
    ++		const void *: (const char *) 0,                       \
    ++		void *:       (char *) 0                              \
     +	)                                                             \
     +)
     +
    @@ include/string.h: size_t strlen (const char *);
      char *strerror (int);
      
     +#if __STDC_VERSION__ >= 202311L
    -+# define strchr(s, chr)    ((__QCharof(s) *) strchr(s, chr))
    -+# define strrchr(s, chr)   ((__QCharof(s) *) strrchr(s, chr))
    -+# define strpbrk(s, chrs)  ((__QCharof(s) *) strpbrk(s, chrs))
    -+# define strstr(s, str)    ((__QCharof(s) *) strstr(s, str))
    ++# define memchr(p, chr)    ((__QVoidptrof(p)) memchr(p, chr))
    ++# define strchr(s, chr)    ((__QCharptrof(s)) strchr(s, chr))
    ++# define strrchr(s, chr)   ((__QCharptrof(s)) strrchr(s, chr))
    ++# define strpbrk(s, chrs)  ((__QCharptrof(s)) strpbrk(s, chrs))
    ++# define strstr(s, str)    ((__QCharptrof(s)) strstr(s, str))
     +#endif
     +
      #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
      #include <strings.h>
      #endif
    +@@ include/string.h: char *strerror_l (int, locale_t);
    + int strcoll_l (const char *, const char *, locale_t);
    + size_t strxfrm_l (char *__restrict, const char *__restrict, size_t, locale_t);
    + void *memmem(const void *, size_t, const void *, size_t);
    ++# if __STDC_VERSION__ >= 202311L
    ++#  define memmem(p, mem)  ((__QVoidptrof(p)) memmem(p, mem))
    ++# endif
    + #endif
    + 
    + #if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
     @@ include/string.h: char *strchrnul(const char *, int);
      char *strcasestr(const char *, const char *);
      void *memrchr(const void *, int, size_t);
      void *mempcpy(void *, const void *, size_t);
     +# if __STDC_VERSION__ >= 202311L
    -+#  define strchrnul(s, chr)   ((__QCharof(s) *) strchrnul(s, chr))
    -+#  define strcasestr(s, str)  ((__QCharof(s) *) strcasestr(s, str))
    ++#  define strchrnul(s, chr)   ((__QCharptrof(s)) strchrnul(s, chr))
    ++#  define strcasestr(s, str)  ((__QCharptrof(s)) strcasestr(s, str))
    ++#  define memrchr(p, chr)     ((__QVoidptrof(p)) memrchr(p, chr))
     +# endif
      #endif
      
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v4 1/1] include/string.h: Implement QChar and QVoid wrappers standardized in C23
  2025-11-27 16:27 ` [musl] [PATCH v4 0/1] include/string.h: Implement QChar and QVoid " Alejandro Colomar
@ 2025-11-27 16:27   ` Alejandro Colomar
  2025-11-29 13:53     ` Luca Kellermann
  0 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-27 16:27 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar, Markus Wichmann

Co-authored-by: Markus Wichmann <nullplan@gmx.net>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/string.h | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/include/string.h b/include/string.h
index 83e2b946..454d93a7 100644
--- a/include/string.h
+++ b/include/string.h
@@ -24,6 +24,15 @@ extern "C" {
 
 #include <bits/alltypes.h>
 
+#define __QVoidptrof(p)  typeof(1?(p):(void*)"")
+#define __QCharptrof(s)  typeof                                       \
+(                                                                     \
+	_Generic(__QVoidptrof(s),                                     \
+		const void *: (const char *) 0,                       \
+		void *:       (char *) 0                              \
+	)                                                             \
+)
+
 void *memcpy (void *__restrict, const void *__restrict, size_t);
 void *memmove (void *, const void *, size_t);
 void *memset (void *, int, size_t);
@@ -55,6 +64,14 @@ size_t strlen (const char *);
 
 char *strerror (int);
 
+#if __STDC_VERSION__ >= 202311L
+# define memchr(p, chr)    ((__QVoidptrof(p)) memchr(p, chr))
+# define strchr(s, chr)    ((__QCharptrof(s)) strchr(s, chr))
+# define strrchr(s, chr)   ((__QCharptrof(s)) strrchr(s, chr))
+# define strpbrk(s, chrs)  ((__QCharptrof(s)) strpbrk(s, chrs))
+# define strstr(s, str)    ((__QCharptrof(s)) strstr(s, str))
+#endif
+
 #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
 #include <strings.h>
 #endif
@@ -74,6 +91,9 @@ char *strerror_l (int, locale_t);
 int strcoll_l (const char *, const char *, locale_t);
 size_t strxfrm_l (char *__restrict, const char *__restrict, size_t, locale_t);
 void *memmem(const void *, size_t, const void *, size_t);
+# if __STDC_VERSION__ >= 202311L
+#  define memmem(p, mem)  ((__QVoidptrof(p)) memmem(p, mem))
+# endif
 #endif
 
 #if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
@@ -95,6 +115,11 @@ char *strchrnul(const char *, int);
 char *strcasestr(const char *, const char *);
 void *memrchr(const void *, int, size_t);
 void *mempcpy(void *, const void *, size_t);
+# if __STDC_VERSION__ >= 202311L
+#  define strchrnul(s, chr)   ((__QCharptrof(s)) strchrnul(s, chr))
+#  define strcasestr(s, str)  ((__QCharptrof(s)) strcasestr(s, str))
+#  define memrchr(p, chr)     ((__QVoidptrof(p)) memrchr(p, chr))
+# endif
 #endif
 
 #ifdef __cplusplus
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v2 1/1] include/string.h: Implement QChar wrappers standardized in C23
  2025-11-27 15:01                   ` Markus Wichmann
  2025-11-27 15:59                     ` Alejandro Colomar
@ 2025-11-27 17:05                     ` Rich Felker
  1 sibling, 0 replies; 22+ messages in thread
From: Rich Felker @ 2025-11-27 17:05 UTC (permalink / raw)
  To: Markus Wichmann; +Cc: musl, Alejandro Colomar

On Thu, Nov 27, 2025 at 04:01:56PM +0100, Markus Wichmann wrote:
> Am Wed, Nov 26, 2025 at 04:18:25PM -0500 schrieb Rich Felker:
> > Hmm, maybe it doesn't work. It looks like only exactly (void *)0 is a
> > null pointer constant. A cast to a qualified void pointer type isn't
> > one. I'm not sure if it's fixable.
> > 
> > Rich
> 
> There really is no expression that turns void* into char*, or at least
> not that I could find. But it is possible to simplify the generic
> expression Alejandro is using:
> 
> #define _QChar(s) typeof(_Generic(1?(s):(void *)"", void *: (char *)0, const void *: (const char *)0))
> #define strstr(s, c) ((_QChar(s))strstr(s, c))
> 
> Feel free to reformat as you like.
> 
> The controlling expression of the generic is compatible with all pointer
> types, and always becomes a void* or const void*. This means it already

Strictly speaking it could also become a pointer to a worse-qualified
version of void like const restrict volatile void *.

Of course passing a such-qualified pointer is a constraint violation
already so it doesn't matter here.

> has the correct type for the mem* functions and bsearch(), once you
> start looking at those. For the str* functions, the only way I see to
> get from those to char* is the generic expression.
> 
> The idea is to use an expression to query the const qualifier only.
> According to the rules for conditional operators, I will get an equally
> qualified void pointer only if the third expression is a non-null void
> pointer (I would get the original type of s if it were a null pointer
> constant). The simplest non-null pointer I can find is a string
> literal.
> 
> I think it is better to let the const correctness macro only handle the
> return value, because it leads to better error messages. With
> Alejandro's current attempt, something like
> 
> int *p;
> strstr(p, "");
> 
> will lead to an error message about no case existing for int* in the
> generic expression, whereas my solution will leave the original error
> message the compiler would generate from initializing a const char *
> from an int * in place.

I like this a lot better, for all the reasons you explained on top of
it being simpler.

Rich

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v4 1/1] include/string.h: Implement QChar and QVoid wrappers standardized in C23
  2025-11-27 16:27   ` [musl] [PATCH v4 1/1] " Alejandro Colomar
@ 2025-11-29 13:53     ` Luca Kellermann
  2025-11-29 17:31       ` Alejandro Colomar
  0 siblings, 1 reply; 22+ messages in thread
From: Luca Kellermann @ 2025-11-29 13:53 UTC (permalink / raw)
  To: From: Alejandro Colomar; +Cc: musl

On Thu, 27 Nov 2025 17:27:27 +0100 Alejandro Colomar wrote:
> Co-authored-by: Markus Wichmann <nullplan@gmx.net>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> ---
>  include/string.h | 25 +++++++++++++++++++++++++
>  1 file changed, 25 insertions(+)
>
> diff --git a/include/string.h b/include/string.h
> index 83e2b946..454d93a7 100644
> --- a/include/string.h
> +++ b/include/string.h
> @@ -24,6 +24,15 @@ extern "C" {
>
>  #include <bits/alltypes.h>
>
> +#define __QVoidptrof(p)  typeof(1?(p):(void*)"")

Another expression to use instead of (void*)"" would be (void*){}. If
I'm not mistaken, this expression is not a null pointer constant, even
if its value is a null pointer. This would help avoid warnings with
-Wwrite-strings and -Wcast-qual.

> +#define __QCharptrof(s)  typeof                                       \
> +(                                                                     \
> +        _Generic(__QVoidptrof(s),                                     \
> +                const void *: (const char *) 0,                       \
> +                void *:       (char *) 0                              \
> +        )                                                             \
> +)

I think the controlling expression of a generic selection must be an
expression. __QVoidptrof(s) is a type. A compound literal could be
used instead:

_Generic((__QVoidptrof(s)){}, ...)

> [...]

Luca

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [musl] [PATCH v4 1/1] include/string.h: Implement QChar and QVoid wrappers standardized in C23
  2025-11-29 13:53     ` Luca Kellermann
@ 2025-11-29 17:31       ` Alejandro Colomar
  0 siblings, 0 replies; 22+ messages in thread
From: Alejandro Colomar @ 2025-11-29 17:31 UTC (permalink / raw)
  To: Luca Kellermann; +Cc: musl

[-- Attachment #1: Type: text/plain, Size: 2133 bytes --]

Hi Luca,

On Sat, Nov 29, 2025 at 02:53:15PM +0100, Luca Kellermann wrote:
> On Thu, 27 Nov 2025 17:27:27 +0100 Alejandro Colomar wrote:
> > Co-authored-by: Markus Wichmann <nullplan@gmx.net>
> > Signed-off-by: Alejandro Colomar <alx@kernel.org>
> > ---
> >  include/string.h | 25 +++++++++++++++++++++++++
> >  1 file changed, 25 insertions(+)
> >
> > diff --git a/include/string.h b/include/string.h
> > index 83e2b946..454d93a7 100644
> > --- a/include/string.h
> > +++ b/include/string.h
> > @@ -24,6 +24,15 @@ extern "C" {
> >
> >  #include <bits/alltypes.h>
> >
> > +#define __QVoidptrof(p)  typeof(1?(p):(void*)"")
> 
> Another expression to use instead of (void*)"" would be (void*){}. If
> I'm not mistaken, this expression is not a null pointer constant, even
> if its value is a null pointer. This would help avoid warnings with
> -Wwrite-strings and -Wcast-qual.

Ahh, good idea.  That'll be better.

> > +#define __QCharptrof(s)  typeof                                       \
> > +(                                                                     \
> > +        _Generic(__QVoidptrof(s),                                     \
> > +                const void *: (const char *) 0,                       \
> > +                void *:       (char *) 0                              \
> > +        )                                                             \
> > +)
> 
> I think the controlling expression of a generic selection must be an
> expression. __QVoidptrof(s) is a type. A compound literal could be
> used instead:
> 
> _Generic((__QVoidptrof(s)){}, ...)

Ahh, yeah, types are allowed in ISO C2y and recent-enough compilers, but
expressions are more portable.

BTW, for the curious, expressions undergo lvalue conversion, but types
don't, so an expression (const int){} as a generic controlling
expression matches type int, but the type 'const int' would match type
'const int'.

So, yes, I'll apply both suggestions.


Have a lovely night!
Alex

> 
> > [...]
> 
> Luca

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v5 0/1] include/string.h: Implement QChar and QVoid wrappers standardized in C23
  2025-11-25 13:51 [musl] [PATCH v1 0/1] include/string.h: Implement QChar wrappers standardized in C23 Alejandro Colomar
                   ` (3 preceding siblings ...)
  2025-11-27 16:27 ` [musl] [PATCH v4 0/1] include/string.h: Implement QChar and QVoid " Alejandro Colomar
@ 2025-12-01 17:53 ` Alejandro Colomar
  2025-12-01 17:53   ` [musl] [PATCH v5 1/1] " Alejandro Colomar
  4 siblings, 1 reply; 22+ messages in thread
From: Alejandro Colomar @ 2025-12-01 17:53 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar

Hi!

In v5, I've applied both suggestions from Luca Kellermann.  See
range-diff below.

Tested:

	alx@devuan:~/tmp$ cat strchr.c 
	#include <stdio.h>
	#include <string.h>

	int
	main(int argc, const char *argv[argc + 1])
	{
		char *p;


		p = strchr(argv[0], 'u');
		puts(p);

		const char *cp;

		cp = strchr(argv[0], 'u');
		puts(cp);

		&strchr(argv[0], 'u');

		strchr(argv[0], 'u') = NULL;

		int *i;

		i = strchr(i, 'u');
	}
	alx@devuan:~/tmp$ gcc -I /opt/local/musl/libc/qchar/include/ strchr.c 
	strchr.c: In function ‘main’:
	strchr.c:10:11: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
	   10 |         p = strchr(argv[0], 'u');
	      |           ^
	strchr.c:18:9: error: lvalue required as unary ‘&’ operand
	   18 |         &strchr(argv[0], 'u');
	      |         ^
	strchr.c:20:30: error: lvalue required as left operand of assignment
	   20 |         strchr(argv[0], 'u') = NULL;
	      |                              ^
	In file included from strchr.c:2:
	strchr.c:24:20: error: passing argument 1 of ‘strchr’ from incompatible pointer type [-Wincompatible-pointer-types]
	   24 |         i = strchr(i, 'u');
	      |                    ^
	      |                    |
	      |                    int *
	/opt/local/musl/libc/qchar/include/string.h:69:54: note: in definition of macro ‘strchr’
	   69 | # define strchr(s, chr)    ((__QCharptrof(s)) strchr(s, chr))
	      |                                                      ^
	/opt/local/musl/libc/qchar/include/string.h:54:15: note: expected ‘const char *’ but argument is of type ‘int *’
	   54 | char *strchr (const char *, int);
	      |               ^~~~~~~~~~~~
	strchr.c:24:11: error: assignment to ‘int *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types]
	   24 |         i = strchr(i, 'u');
	      |           ^



Have a lovely night!
Alex

Alejandro Colomar (1):
  include/string.h: Implement QChar and QVoid wrappers standardized in
    C23

 include/string.h | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

Range-diff against v4:
1:  b891d212 ! 1:  b93da5be include/string.h: Implement QChar and QVoid wrappers standardized in C23
    @@ include/string.h: extern "C" {
      
      #include <bits/alltypes.h>
      
    -+#define __QVoidptrof(p)  typeof(1?(p):(void*)"")
    ++#define __QVoidptrof(p)  typeof(1?(p):(void*){})
     +#define __QCharptrof(s)  typeof                                       \
     +(                                                                     \
    -+	_Generic(__QVoidptrof(s),                                     \
    ++	_Generic((__QVoidptrof(s)){},                                 \
     +		const void *: (const char *) 0,                       \
     +		void *:       (char *) 0                              \
     +	)                                                             \
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [musl] [PATCH v5 1/1] include/string.h: Implement QChar and QVoid wrappers standardized in C23
  2025-12-01 17:53 ` [musl] [PATCH v5 0/1] " Alejandro Colomar
@ 2025-12-01 17:53   ` Alejandro Colomar
  0 siblings, 0 replies; 22+ messages in thread
From: Alejandro Colomar @ 2025-12-01 17:53 UTC (permalink / raw)
  To: musl; +Cc: Alejandro Colomar, Markus Wichmann

Co-authored-by: Markus Wichmann <nullplan@gmx.net>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/string.h | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/include/string.h b/include/string.h
index 83e2b946..0ca80b60 100644
--- a/include/string.h
+++ b/include/string.h
@@ -24,6 +24,15 @@ extern "C" {
 
 #include <bits/alltypes.h>
 
+#define __QVoidptrof(p)  typeof(1?(p):(void*){})
+#define __QCharptrof(s)  typeof                                       \
+(                                                                     \
+	_Generic((__QVoidptrof(s)){},                                 \
+		const void *: (const char *) 0,                       \
+		void *:       (char *) 0                              \
+	)                                                             \
+)
+
 void *memcpy (void *__restrict, const void *__restrict, size_t);
 void *memmove (void *, const void *, size_t);
 void *memset (void *, int, size_t);
@@ -55,6 +64,14 @@ size_t strlen (const char *);
 
 char *strerror (int);
 
+#if __STDC_VERSION__ >= 202311L
+# define memchr(p, chr)    ((__QVoidptrof(p)) memchr(p, chr))
+# define strchr(s, chr)    ((__QCharptrof(s)) strchr(s, chr))
+# define strrchr(s, chr)   ((__QCharptrof(s)) strrchr(s, chr))
+# define strpbrk(s, chrs)  ((__QCharptrof(s)) strpbrk(s, chrs))
+# define strstr(s, str)    ((__QCharptrof(s)) strstr(s, str))
+#endif
+
 #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
 #include <strings.h>
 #endif
@@ -74,6 +91,9 @@ char *strerror_l (int, locale_t);
 int strcoll_l (const char *, const char *, locale_t);
 size_t strxfrm_l (char *__restrict, const char *__restrict, size_t, locale_t);
 void *memmem(const void *, size_t, const void *, size_t);
+# if __STDC_VERSION__ >= 202311L
+#  define memmem(p, mem)  ((__QVoidptrof(p)) memmem(p, mem))
+# endif
 #endif
 
 #if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
@@ -95,6 +115,11 @@ char *strchrnul(const char *, int);
 char *strcasestr(const char *, const char *);
 void *memrchr(const void *, int, size_t);
 void *mempcpy(void *, const void *, size_t);
+# if __STDC_VERSION__ >= 202311L
+#  define strchrnul(s, chr)   ((__QCharptrof(s)) strchrnul(s, chr))
+#  define strcasestr(s, str)  ((__QCharptrof(s)) strcasestr(s, str))
+#  define memrchr(p, chr)     ((__QVoidptrof(p)) memrchr(p, chr))
+# endif
 #endif
 
 #ifdef __cplusplus
-- 
2.51.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2025-12-01 17:53 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-25 13:51 [musl] [PATCH v1 0/1] include/string.h: Implement QChar wrappers standardized in C23 Alejandro Colomar
2025-11-25 13:51 ` [musl] [PATCH v1 1/1] " Alejandro Colomar
2025-11-25 21:49 ` [musl] [PATCH v2 0/1] " Alejandro Colomar
2025-11-25 21:49   ` [musl] [PATCH v2 1/1] " Alejandro Colomar
2025-11-26  1:34     ` Rich Felker
2025-11-26 12:22       ` Alejandro Colomar
2025-11-26 14:17         ` Rich Felker
2025-11-26 14:56           ` Alejandro Colomar
2025-11-26 15:02             ` Rich Felker
2025-11-26 15:36               ` Alejandro Colomar
2025-11-26 21:18                 ` Rich Felker
2025-11-27 15:01                   ` Markus Wichmann
2025-11-27 15:59                     ` Alejandro Colomar
2025-11-27 17:05                     ` Rich Felker
2025-11-26 21:50 ` [musl] [PATCH v3 0/1] " Alejandro Colomar
2025-11-26 21:50   ` [musl] [PATCH v3 1/1] " Alejandro Colomar
2025-11-27 16:27 ` [musl] [PATCH v4 0/1] include/string.h: Implement QChar and QVoid " Alejandro Colomar
2025-11-27 16:27   ` [musl] [PATCH v4 1/1] " Alejandro Colomar
2025-11-29 13:53     ` Luca Kellermann
2025-11-29 17:31       ` Alejandro Colomar
2025-12-01 17:53 ` [musl] [PATCH v5 0/1] " Alejandro Colomar
2025-12-01 17:53   ` [musl] [PATCH v5 1/1] " Alejandro Colomar

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).