Project

General

Profile

Actions

Bug #20908

closed

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

Added by thesamesam (Sam James) 27 days ago. Updated 20 days 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) 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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0