ISO C 23 on macOS

Bruno Haible via Gnulib discussion list <[email protected]>
Newsgroups gmane.comp.lib.gnulib.bugs
Message-ID <2594711.TTEOE9XjLl@cagnes>
Hi all,

When migrating C programs from C99/C11/C17 to C23, function pointer types
need to be updated:

A function type with parameter list () meant "unspecified arguments".
This does not exist any more in C23, as a parameter list () now means
"no arguments".

One has to use a function type with parameter list (...), which means a
variable number of arguments.

Quite simple and easy, I thought.

But not on macOS. On macOS, functions with a parameter list (...) have
a different calling convention [1]. Code that worked in C17 with ()
does not work in C23 with (...) any more. How to reproduce:

==================================== foo.c ====================================
#include <stdio.h>

typedef int (*old_generic_function_t) ();

#if __STDC_VERSION__ >= 202300 /* Avoid error "ISO C requires a named parameter before '...'" */
typedef int (*new_generic_function_t) (...);
#endif

int
f (int x)
{
  return x + 42;
}

old_generic_function_t fp1 = (old_generic_function_t) f;

#if __STDC_VERSION__ >= 202300
new_generic_function_t fp2 = (new_generic_function_t) f;
#endif

int (*fp3) (int) = f;

int
main ()
{
#if !(defined __cplusplus || __STDC_VERSION__ >= 202300) /* Avoid error "too many arguments to function" */
  printf ("Calling f through old_generic_function_t: %d\n", fp1 (100));
#endif
#if __STDC_VERSION__ >= 202300
  printf ("Calling f through new_generic_function_t: %d\n", fp2 (1000));
#endif
  printf ("Calling f through precise function type:  %d\n", fp3 (10000));
}
===============================================================================

On all other platforms:

$ gcc -std=c17 foo.c
$ ./a.out
Calling f through old_generic_function_t: 142
Calling f through precise function type:  10042

$ gcc -std=c23 foo.c
$ ./a.out
Calling f through new_generic_function_t: 1042
Calling f through precise function type:  10042

With Apple cc (clang) on macOS/arm64:

$ cc -std=c17 foo.c
foo.c:27:65: warning: passing arguments to a function without a prototype is deprecated in all versions of C and is not supported in C23
      [-Wdeprecated-non-prototype]
   27 |   printf ("Calling f through old_generic_function_t: %d\n", fp1 (100));
      |                                                                 ^
1 warning generated.
$ ./a.out
Calling f through old_generic_function_t: 142
Calling f through precise function type:  10042

$ cc -std=c23 foo.c
$ ./a.out
Calling f through new_generic_function_t: 43          <== UNEXPECTED! should be 1042.
Calling f through precise function type:  10042

With /opt/homebrew/bin/gcc-16 on macOS/arm64:

$ /opt/homebrew/bin/gcc-16 -std=c17 foo.c
$ ./a.out
Calling f through old_generic_function_t: 142
Calling f through precise function type:  10042

$ /opt/homebrew/bin/gcc-16 -std=c23 foo.c
$ ./a.out
Calling f through new_generic_function_t: 14747170    <== UNEXPECTED! should be 1042.
Calling f through precise function type:  10042

What has changed is the argument passing convention:
An integer argument gets passed to a () function in $x0,
but to a (...) function on the stack.

Does anyone see a workaround?

It does affect GNU libffcall: the 'trampoline' API on macOS/C23 is broken.

It may also affect libffi; I don't know.

I suspect that it also affects many other packages that use "generic"
function types in one form or the other.

Bruno

[1] https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.