Project

General

Profile

ActionsLike0

Bug #20908

closed

Ruby extension builds fail with GCC 15 which defaults to -std=gnu23

Added by thesamesam (Sam James) about 2 months ago. Updated about 1 month ago.

Status:
Third Party's Issue
Assignee:
-
Target version:
-
[ruby-core:120002]

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) about 2 months ago

cc @shyouhei (Shyouhei Urabe) who made other C23 porting changes

Updated by shyouhei (Shyouhei Urabe) about 2 months 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) about 2 months ago

Can't we prioritize the condition defined(rb_define_method) for RUBY_METHOD_FUNC?

Updated by shyouhei (Shyouhei Urabe) about 2 months ago

Ah, that could be a nice idea. Not sure if that breaks other parts though.

Updated by hansdegraaff (Hans de Graaff) about 1 month 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) about 1 month 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.

ActionsLike0

Also available in: Atom PDF