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

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@...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);
> 
> 		_Generic(typeof(1?(void *)      0:""), const char *: 0);
> 		_Generic(typeof(1?(const void *)0:""),       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);
> 	      |                  ^~~~~~
> 	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

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.