ta_yano
@ta_yano

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

gccの15バグってませんか?

Discussion

解決したいこと

明示的に引数を指定しない関数に引数を指定する

clangがCの仕様的に正しい動きだと思います
知見のある人ご意見ください

発生している問題・エラー

tyano@fedora:~$ cat test.c 
void d(){}
int main(void) {d(1);return 0;}
tyano@fedora:~$ gcc test.c 
test.c: In function ‘main’:
test.c:2:17: error: too many arguments to function ‘d’; expected 0, have 1
    2 | int main(void) {d(1);return 0;}
      |                 ^ ~
test.c:1:6: note: declared here
    1 | void d(){}
      |      ^
tyano@fedora:~$ clang test.c
test.c:2:20: warning: too many arguments in call to 'd'
    2 | int main(void) {d(1);return 0;}
      |                 ~  ^
test.c:2:18: warning: passing arguments to 'd' without a prototype is deprecated in all versions of C and is not supported in C23 [-Wdeprecated-non-prototype]
    2 | int main(void) {d(1);return 0;}
      |                  ^
2 warnings generated.

該当するソースコード

tyano@fedora:~$ gcc --version
gcc (GCC) 15.0.1 20250313 (Red Hat 15.0.1-0)
Copyright (C) 2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

tyano@fedora:~$ clang --version
clang version 20.1.1 (Fedora 20.1.1-1.fc43)
Target: x86_64-redhat-linux-gnu
Thread model: posix
InstalledDir: /usr/lib64/llvm20/bin
Configuration file: /etc/clang/x86_64-redhat-linux-gnu-clang.cfg

自分で試したこと

voidを指定するとclangでもエラーになります

tyano@fedora:~$ cat test_void.c
void d(void){}
int main(void) {d(1);return 0;}
tyano@fedora:~$ gcc test_void.c 
test_void.c: In function ‘main’:
test_void.c:2:17: error: too many arguments to function ‘d’; expected 0, have 1
    2 | int main(void) {d(1);return 0;}
      |                 ^ ~
test_void.c:1:6: note: declared here
    1 | void d(void){}
      |      ^
tyano@fedora:~$ clang test_void.c 
test_void.c:2:19: error: too many arguments to function call, expected 0, have 1
    2 | int main(void) {d(1);return 0;}
      |                 ~ ^
test_void.c:1:6: note: 'd' declared here
    1 | void d(void){}
      |      ^
1 error generated.

これで何が問題かというと
GNUのライブラリ関連が./configureでビルドエラーになるので
結構影響大きいと思うんですよね

2

言語仕様的には関数定義の仮引数と関数呼出しの実引数の辻褄があっていることを要求しており、そうでないときは未定義です。 C99 では 6.5.2.2 に記述があります。

未定義については規格が何ら要求を課さないので何が起きてもかまいません。 エラーとして検出するのも正しい動作のひとつであると考えられます。 関数原型がないことによって結果としてはコンパイラがエラーとして検出しにくくなりますが、エラーにしてはならないというわけではないです。

現代的にはエラーとして検出されるのが好ましい動作と考える人が多いでしょう。

2Like

なるほど。gcc的には悪くないと

>現代的にはエラーとして検出されるのが好ましい動作と考える人が多いでしょう。
gcc単体で見るとそうかもしれませんが
ビルド出来なくなるライブラリが多くなるのを好ましいと考える人は多くないと思います。

autoconf直してライブラリもautoconfかけ直して更新する?
結構大変だと思うのですが

以下で入ったっぽいですね

commit a236f70617213343f3075ee43e8d9f5882dca400

-> 間違い 55e3bd376b2214e200fa76d12b67ff259b06c212でした

PR118112見てみましたが
既存のライブラリのビルドが出来なくなることについては
誰も言及していないようです。
知ったこっちゃねーよって感じなんですかね

0Like

GCC 15 でこの場合をエラーとするのはデフォルトの規格が gnu23 (C23 に GNU 拡張を付け加えたもの) に変えられた結果のようですね。 C99 ではなく C23 の説明をすべきでした。

C23 では空の仮引数リストは関数原型がないという意味ではなく (void) と同じ、つまり仮引数がないという意味に変更されました。

関数原型がない形での関数宣言・定義は規格成立当初 (C89) から削除予定とされていて、三十年以上の猶予期間を経て C23 でようやく実際に削除されたという扱いです。 しかし、 GCC ではまだ以前の規格 (C17 以前) を指定できますし、これはちょっと度を越して気の長い穏当な処置と言えると思います。

2Like

まとめるとこんな感じでしょうか?

以下のように引数の指定のない関数に対して引数を追加してコールした場合

void d(){}
int main(void){d(1);return 0;}
仕様 コンパイラ 内容
C89 - エラーとしてはいけない
C99 - 未定義
C23 - エラーにする
C17? gcc-14 warning (GCC 14.1 released [2024-05-07])
? clang-20.1.1 warning (20.1.1 / 19 March 2025)
C23 gcc-15 error (未リリース)

gccの修正差分
55e3bd376b2214e200fa76d12b67ff259b06c212

commit 55e3bd376b2214e200fa76d12b67ff259b06c212 (HEAD)
Author: Joseph Myers <josmyers@redhat.com>
Date:   Fri Nov 15 23:42:59 2024 +0000

    c: Default to -std=gnu23
    
    Change the default language version for C compilation from -std=gnu17
    to -std=gnu23.  A few tests are updated to remove local definitions of
    bool, true and false (where making such an unconditional test change
    seemed to make more sense than changing the test conditionally earlier
    or building it with -std=gnu17); most test issues were already
    addressed in previous patches.  In the case of
    ctf-function-pointers-2.c, it was agreed in bug 117289 that it would
    be OK to put -std=gnu17 in the test and leave more optimal BTF / CTF
    output for this test as a potential future improvement.
    
    Since the original test fixes, more such fixes have become necessary
    and so are included in this patch.  More noinline attributes are added
    to simulate-thread tests where () meaning a prototype affected test
    results, while gcc.dg/torture/pr117496-1.c (a test declaring a
    function with () then calling it with arguments) gets -std=gnu17
    added.
    
    Bootstrapped with no regressions for x86_64-pc-linux-gnu.
...<snip>   

影響があると思われる部分
gcc/c-family/c-opts.cc

/* Common initialization before calling option handlers.  */
void
c_common_init_options (unsigned int decoded_options_count,
               struct cl_decoded_option *decoded_options)

...
  if (c_language == clk_c)
    {
      /* The default for C is gnu23.  */
      set_std_c23 (false /* ISO */);

55e3bd376b2214e200fa76d12b67ff259b06c212 でエラーになり
f242f79b8afeec58477e99c44530bd503878c6d5 で問題ないことも確認済み

経緯とかは理解出来たけど
問題はGNUのライブラリがGNUコンパイラコレクションで
デフォルト設定だとコンパイル出来ないこと

自分はbuildrootくらいしか使わないので
CFLAGSに-std=gnu17(またはそれ以前)つけていくくらいかなぁ

他でライブラリビルドしてる人とかどうするんだろう

1Like

その例の場合を規格に基づいて言うなら C89 においても動作は未定義であると明記されている状況に該当します。

もしそのように書いているプログラムがあったなら元から間違ったプログラムであり、ビルドが通ったとしてもそれは間違いが検出されていないだけで意図通りに動く保証がありません。

呼出し規約が cdecl なら実引数が多い分には無視されるだけになりそうですが最適化をかけたときの挙動は予想できませんし、基本的には修正せずに使おうとすべきではないです。

0Like

>もしそのように書いているプログラムがあったなら元から間違ったプログラムであり、ビルドが通ったとしてもそれは間違いが検出されていないだけで意図通りに動く保証がありません。

autoconf使っているのはだいたいそうですよ?

プログラム本体がそういう作りになっているわけではないですが
ライブラリを更新するかCFLAGS変更するかしないといけないと思うんです

以下はGNUの名前がついているgmpの例です

tyano oss$ wget https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz
Saving 'gmp-6.3.0.tar.xz'
HTTP response 200  [https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz]
gmp-6.3.0.tar.xz     100% [============================================>]    1.99M   77.48KB/s
                          [Files: 1  Bytes: 1.99M [51.68KB/s] Redirects:]
tyano oss$ tar xf gmp-6.3.0.tar.xz 
tyano oss$ cd gmp-6.3.0/
tyano gmp-6.3.0$ ./configure 
checking build system type... nehalem-pc-linux-gnu
checking host system type... nehalem-pc-linux-gnu
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking whether to enable maintainer-specific portions of Makefiles... no
checking ABI=64
checking compiler gcc -O2 -pedantic -fomit-frame-pointer -m64 ... no, long long reliability test 1
checking whether cc is gcc... yes
checking compiler cc -O2 -pedantic -fomit-frame-pointer -m64 ... no, long long reliability test 1
checking ABI=x32
checking compiler gcc -O2 -pedantic -fomit-frame-pointer -mx32 ... no
checking whether cc is gcc... yes
checking compiler cc -O2 -pedantic -fomit-frame-pointer -mx32 ... no
checking ABI=32
checking compiler gcc -m32 -O2 -pedantic -fomit-frame-pointer ... no
checking compiler gcc -O2 -pedantic -fomit-frame-pointer ... no, long long reliability test 1
checking compiler icc -no-gcc ... no
checking whether cc is gcc... yes
checking compiler cc -m32 -O2 -pedantic -fomit-frame-pointer ... no
checking compiler cc -O2 -pedantic -fomit-frame-pointer ... no, long long reliability test 1
configure: error: could not find a working compiler, see config.log for details

configureのここ g()のコールでエラーになります

 6561 if test "$gmp_prog_cc_works" = yes; then
 6562   # remove anything that might look like compiler output to our "||" expression
 6563   rm -f conftest* a.out b.out a.exe a_out.exe
 6564   cat >conftest.c <<EOF
 6565 /* The following provokes a segfault in the compiler on powerpc-apple-darwin.
 6566    Extracted from tests/mpn/t-iord_u.c.  Causes Apple's gcc 3.3 build 1640 and
 6567    1666 to segfault with e.g., -O2 -mpowerpc64.  */
 6568 
 6569 #if defined (__GNUC__) && ! defined (__cplusplus)
 6570 typedef unsigned long long t1;typedef t1*t2;
 6571 void g(){}
 6572 void h(){}
 6573 static __inline__ t1 e(t2 rp,t2 up,int n,t1 v0)
 6574 {t1 c,x,r;int i;if(v0){c=1;for(i=1;i<n;i++){x=up[i];r=x+1;rp[i]=r;}}return c;}
 6575 void f(){static const struct{t1 n;t1 src[9];t1 want[9];}d[]={{1,{0},{1}},};t1 got[9];int i;
 6576 for(i=0;i<1;i++){if(e(got,got,9,d[i].n)==0)h();g(i,d[i].src,d[i].n,got,d[i].want,9);if(d[i].n)h();}}
 6577 #else
 6578 int dummy;
 6579 #endif
 6580 
 6581 int main () { return 0; }
 6582 EOF
 6583   echo "Test compile: long long reliability test 1" >&5
 6584   gmp_compile="$cc $cflags $cppflags conftest.c >&5"
 6585   if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5

0Like

「Cの仕様的に」というところから話がスタートしたのでそれに対して (言語仕様を中心にして判断するなら) 元からそれは正しくないと私は意見しました。

しかし現実にはそうも言ってられない事情が色々とあるのも確かです。 規格として何が正しいとしてもどこかを変えれば関連するところは足並みを揃えて修正していくしか仕方がありません。 それがまだ途上ということなんでしょう。

かつて GCC 4 で大きな変革があったときに Linux のコンパイルが通るように修正するのが大手間だったらしいですし、たまにはそういうこともあります。

0Like

warningを出し続けてきたのに対応していないライブラリを、warningが出続けていたのに使い続けるリスクの方が大きいんじゃないですかね。

0Like

autotoolsの問題であれば、実はすでに対応が済んでいて、 autoreconf をして configure スクリプトを作り直せば解決する、なんてことはないですかね。

CFLAGSに-std=gnu17(またはそれ以前)つけていくくらいかなぁ

CCで古いgccを指定する、という方法もあると思います。
linuxディストリビューションであれば、ある程度の古いバージョンのgccも残してあるようですし。

0Like

Your answer might help someone💌