Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <xgfp7hb5fdsxzp2htxjlcj4kfvrecl5w7jhhw3kay3iqj2kxpn@wun6pkdsgv6n>
Date: Wed, 26 Nov 2025 13:22:17 +0100
From: Alejandro Colomar <alx@...nel.org>
To: Rich Felker <dalias@...c.org>
Cc: musl@...ts.openwall.com
Subject: Re: [PATCH v2 1/1] include/string.h: Implement QChar wrappers
 standardized in C23

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@...uan:~/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@...uan:~/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@...uan:~/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@...uan:~/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@...uan:~/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/>).

Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.