Bug #20908
closedRuby extension builds fail with GCC 15 which defaults to -std=gnu23
Description
Hi!
Upcoming GCC 15 defaults to C23 (-std=gnu23
). One thing C23 changes is removing unprototyped functions, so void foo()
now means void foo(void)
, rather than "any arguments".
Ruby extensions fail to build as a result with GCC 15. This was reported downstream in Gentoo at https://bugs.gentoo.org/943784 where brotli-0.6.0 is an example:
make: Entering directory '/var/tmp/portage/dev-ruby/brotli-0.6.0/work/ruby32/brotli-0.6.0/ext/brotli'
x86_64-pc-linux-gnu-gcc -I. -I/usr/include/ruby-3.2.0/x86_64-linux -I/usr/include/ruby-3.2.0/ruby/backward -I/usr/include/ruby-3.2.0 -I. -DHAVE_BROTLI_DECODE_H -DHAVE_BROTLI_ENCODE_H -fPIC -O2 -pipe -march=native -fno-diagnostics-color -o brotli.o -c brotli.c
In file included from /usr/include/ruby-3.2.0/ruby/ruby.h:27,
from /usr/include/ruby-3.2.0/ruby.h:38,
from brotli.h:4,
from brotli.c:1:
brotli.c: In function ‘Init_brotli’:
/usr/include/ruby-3.2.0/ruby/internal/anyargs.h:363:45: error: passing argument 3 of ‘rb_define_singleton_method_m1’ from incompatible pointer type [-Wincompatible-pointer-types]
363 | # define RUBY_METHOD_FUNC(func) RBIMPL_CAST((VALUE (*)(ANYARGS))(func))
| ^
| |
| VALUE (*)(void) {aka long unsigned int (*)(void)}
/usr/include/ruby-3.2.0/ruby/internal/anyargs.h:308:144: note: in definition of macro ‘rb_define_singleton_method’
308 | #define rb_define_singleton_method(obj, mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method((arity), (func))((obj), (mid), (func), (arity))
| ^~~~
/usr/include/ruby-3.2.0/ruby/internal/anyargs.h:363:33: note: in expansion of macro ‘RBIMPL_CAST’
363 | # define RUBY_METHOD_FUNC(func) RBIMPL_CAST((VALUE (*)(ANYARGS))(func))
| ^~~~~~~~~~~
brotli.c:478:55: note: in expansion of macro ‘RUBY_METHOD_FUNC’
478 | rb_define_singleton_method(rb_mBrotli, "deflate", RUBY_METHOD_FUNC(brotli_deflate), -1);
| ^~~~~~~~~~~~~~~~
/usr/include/ruby-3.2.0/ruby/internal/anyargs.h:271:21: note: expected ‘VALUE (*)(int, union <anonymous>, VALUE)’ {aka ‘long unsigned int (*)(int, union <anonymous>, long unsigned int)’} but argument is of type ‘VALUE (*)(void)’ {aka ‘long unsigned int (*)(void)’}
271 | RBIMPL_ANYARGS_DECL(rb_define_singleton_method, VALUE, const char *)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/ruby-3.2.0/ruby/internal/anyargs.h:254:41: note: in definition of macro ‘RBIMPL_ANYARGS_DECL’
254 | RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m1(__VA_ARGS__, VALUE(*)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE), int); \
| ^~~
The ANYARGS
macro can't work in its current form, as defined at e.g. https://github.com/ruby/ruby/blob/f127bcb8294fd417c253dd7acab3ff3b9f0bf555/parser_st.h#L45:
#ifndef ANYARGS
# ifdef __cplusplus
# define ANYARGS ...
# else
# define ANYARGS
# endif
#endif
... because of the change in C23 I mentioned above, i.e. (ANYARGS)
being ()
now means no arguments, not any.
I note that Ruby was adapted in part already for this change, see e.g. https://github.com/ruby/ruby/commit/4e64edb6cd8d1b444c591bfd50ec3d357e794f6e, but it appears that the headers that extensions need to build against aren't yet ready.
Updated by thesamesam (Sam James) 27 days ago
cc @shyouhei (Shyouhei Urabe) who made other C23 porting changes
Updated by shyouhei (Shyouhei Urabe) 26 days ago
- Status changed from Open to Third Party's Issue
The extension library is confusing our C level API. rb_define_singleton_method()
and friends do not intend to take RUBY_METHOD_FUNC
. That had accidentally worked because of the past C specifications you described, but not any longer in C23.
Can you inform the extension authors that passing bare function pointers should just work here, like this? :
From 8e589adb92a7d5c8c9e8d35bb116ad5ab0686e30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?=
<shyouhei@ruby-lang.org>
Date: Tue, 26 Nov 2024 13:42:54 +0900
Subject: [PATCH] stop worrying and just pass bare function
`rb_define_method` etc. already take care arities. You don't have
to apply RUBY_METHOD_FUNC.
---
ext/brotli/brotli.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/ext/brotli/brotli.c b/ext/brotli/brotli.c
index cb69500..a9cc23d 100644
--- a/ext/brotli/brotli.c
+++ b/ext/brotli/brotli.c
@@ -475,9 +475,9 @@ Init_brotli(void)
rb_mBrotli = rb_define_module("Brotli");
rb_eBrotli = rb_define_class_under(rb_mBrotli, "Error", rb_eStandardError);
rb_global_variable(&rb_eBrotli);
- rb_define_singleton_method(rb_mBrotli, "deflate", RUBY_METHOD_FUNC(brotli_deflate), -1);
- rb_define_singleton_method(rb_mBrotli, "inflate", RUBY_METHOD_FUNC(brotli_inflate), 1);
- rb_define_singleton_method(rb_mBrotli, "version", RUBY_METHOD_FUNC(brotli_version), 0);
+ rb_define_singleton_method(rb_mBrotli, "deflate", brotli_deflate, -1);
+ rb_define_singleton_method(rb_mBrotli, "inflate", brotli_inflate, 1);
+ rb_define_singleton_method(rb_mBrotli, "version", brotli_version, 0);
id_read = rb_intern("read");
// Brotli::Writer
id_write = rb_intern("write");
@@ -485,9 +485,9 @@ Init_brotli(void)
id_close = rb_intern("close");
rb_Writer = rb_define_class_under(rb_mBrotli, "Writer", rb_cObject);
rb_define_alloc_func(rb_Writer, rb_writer_alloc);
- rb_define_method(rb_Writer, "initialize", RUBY_METHOD_FUNC(rb_writer_initialize), -1);
- rb_define_method(rb_Writer, "write", RUBY_METHOD_FUNC(rb_writer_write), 1);
- rb_define_method(rb_Writer, "finish", RUBY_METHOD_FUNC(rb_writer_finish), 0);
- rb_define_method(rb_Writer, "flush", RUBY_METHOD_FUNC(rb_writer_flush), 0);
- rb_define_method(rb_Writer, "close", RUBY_METHOD_FUNC(rb_writer_close), 0);
+ rb_define_method(rb_Writer, "initialize", rb_writer_initialize, -1);
+ rb_define_method(rb_Writer, "write", rb_writer_write, 1);
+ rb_define_method(rb_Writer, "finish", rb_writer_finish, 0);
+ rb_define_method(rb_Writer, "flush", rb_writer_flush, 0);
+ rb_define_method(rb_Writer, "close", rb_writer_close, 0);
}
--
2.47.0
Updated by nobu (Nobuyoshi Nakada) 26 days ago
Can't we prioritize the condition defined(rb_define_method)
for RUBY_METHOD_FUNC
?
Updated by shyouhei (Shyouhei Urabe) 26 days ago
Ah, that could be a nice idea. Not sure if that breaks other parts though.
Updated by hansdegraaff (Hans de Graaff) 21 days ago
We initially opened this bug because we had multiple cases of this issue and the one in brotli was an example. Another case shows up in io-console:
x86_64-pc-linux-gnu-gcc -I. -I/usr/include/ruby-3.1.0/x86_64-linux -I/usr/include/ruby-3.1.0/ruby/backward -I/usr/include/ruby-3.1.0 -I. -DHAVE_RB_IO_DESCRIPTOR -DHAVE_RB_IO_GET_WRITE_IO -DHAVE_TERMIOS_H -DHAVE_CFMAKERAW -DHAVE_SYS_IOCTL_H -DHAVE_RB_IO_WAIT=1 -fPIC -std=gnu23 -o console.o -c console.c
In file included from /usr/include/ruby-3.1.0/ruby/ruby.h:26,
from /usr/include/ruby-3.1.0/ruby.h:38,
from console.c:9:
console.c: In function ‘InitVM_console’:
/usr/include/ruby-3.1.0/ruby/internal/anyargs.h:287:135: error: passing argument 3 of ‘rb_define_method_m3’ from incompatible pointer type [-Wincompatible-pointer-types]
287 | #define rb_define_method(klass, mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_method((arity), (func))((klass), (mid), (func), (arity))
| ^~~~~~
| |
| __attribute__((noreturn)) VALUE (*)(int, const VALUE *, VALUE, VALUE) {aka __attribute__((noreturn)) long unsigned int (*)(int, const long unsigned int *, long unsigned int, long unsigned int)}
console.c:1815:5: note: in expansion of macro ‘rb_define_method’
1815 | rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1);
| ^~~~~~~~~~~~~~~~
console_key_pressed_p
is defined as rb_f_notimplement
and RUBY_METHOD_FUNC
is not used anywhere in this gem.
Updated by shyouhei (Shyouhei Urabe) 20 days ago
Thank you for the report anyways! It's totally fine.
The io-console build log reads that it builds against ruby 3.1, which is a bit older than C23. It didn't know the breaking C change.
It's now continuously integrated against (clang's) C23 and builds well (see: https://github.com/ruby/ruby/actions/runs/12104018027/job/33746931422#step:8:1534 ). I guess newer versions could have rerouted the problem.