Re: git: 43703bc489ec - main - stdlib.h: Fix qsort_r compatibility with GCC 12.

From: Jessica Clarke <jrtc27_at_freebsd.org>
Date: Thu, 19 Jan 2023 23:31:30 UTC
On 19 Jan 2023, at 23:11, Jessica Clarke <jrtc27@FreeBSD.org> wrote:
> 
> On 19 Jan 2023, at 22:49, John Baldwin <jhb@FreeBSD.org> wrote:
>> 
>> The branch main has been updated by jhb:
>> 
>> URL: https://cgit.FreeBSD.org/src/commit/?id=43703bc489ec504b947b869045c492ed38c1a69c
>> 
>> commit 43703bc489ec504b947b869045c492ed38c1a69c
>> Author:     John Baldwin <jhb@FreeBSD.org>
>> AuthorDate: 2023-01-19 22:48:52 +0000
>> Commit:     John Baldwin <jhb@FreeBSD.org>
>> CommitDate: 2023-01-19 22:48:52 +0000
>> 
>>   stdlib.h: Fix qsort_r compatibility with GCC 12.
>> 
>>   GCC 12 (unlike GCC 9) does not match a function argument passed to the
>>   old qsort_r() API (as is used in the qsort_r_compat test) to a
>>   function pointer type via __generic.  It treats the function type as a
>>   distinct type from a function pointer.  As a workaround, add a second
>>   definition of qsort_r for GCC 12 which uses the bare function type.
> 
> As far as I can tell both versions of GCC behave the same. The
> difference is whether __generic is using _Generic or
> __builtin_choose_expr with __builtin_types_compatible_p, since function
> and function pointer types are not compatible. Clang will take the
> __has_extension path, but GCC will take the builtins path, and so Clang
> works but GCC doesn’t.
> 
> As a result of this change you’ve likely broken code that does
> qsort_r(..., &f) as that will have the opposite problem. The right fix
> is to force arg5 to decay, such as by (ab)using the comma operator with
> __generic((0, arg5), ...). I guess that probably belongs in the
> fallback implementation of __generic though, not here, which would give
> the following real fix:
> 
> diff --git a/sys/sys/cdefs.h b/sys/sys/cdefs.h
> index 83ba7584e5b9..f7eff4768151 100644
> --- a/sys/sys/cdefs.h
> +++ b/sys/sys/cdefs.h
> @@ -312,6 +312,9 @@
>  * __generic().  Unlike _Generic(), this macro can only distinguish
>  * between a single type, so it requires nested invocations to
>  * distinguish multiple cases.
> + *
> + * Note that the comma operator is used to force expr to decay in order to
> + * match _Generic.
>  */
> 
> #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
> @@ -321,7 +324,7 @@
> #elif __GNUC_PREREQ__(3, 1) && !defined(__cplusplus)
> #define	__generic(expr, t, yes, no)					\
> 	__builtin_choose_expr(						\
> -	    __builtin_types_compatible_p(__typeof(expr), t), yes, no)
> +	    __builtin_types_compatible_p(__typeof((0, expr)), t), yes, no)

With (expr) instead of expr, of course...

Jess

> #endif
> 
> /*
> 
> Does that work instead for you after reverting this commit?
> 
> Jess
> 
>>   Reviewed by:    emaste
>>   Differential Revision:  https://reviews.freebsd.org/D37410
>> ---
>> include/stdlib.h | 6 ++++++
>> 1 file changed, 6 insertions(+)
>> 
>> diff --git a/include/stdlib.h b/include/stdlib.h
>> index 754e8f5f5fd4..30d24aea1c10 100644
>> --- a/include/stdlib.h
>> +++ b/include/stdlib.h
>> @@ -352,9 +352,15 @@ void __qsort_r_compat(void *, size_t, size_t, void *,
>> __sym_compat(qsort_r, __qsort_r_compat, FBSD_1.0);
>> #endif
>> #if defined(__generic) && !defined(__cplusplus)
>> +#if __GNUC__ == 12
>> +#define	qsort_r(base, nel, width, arg4, arg5)				\
>> +    __generic(arg5, int (void *, const void *, const void *),		\
>> +	__qsort_r_compat, qsort_r)(base, nel, width, arg4, arg5)
>> +#else
>> #define	qsort_r(base, nel, width, arg4, arg5)				\
>>    __generic(arg5, int (*)(void *, const void *, const void *),	\
>>        __qsort_r_compat, qsort_r)(base, nel, width, arg4, arg5)
>> +#endif
>> #elif defined(__cplusplus)
>> __END_DECLS
>> extern "C++" {