👀

GCCのバグに遭遇した話

2023/10/29に公開

バグに遭遇した経緯

競技プログラミングで使用するライブラリとして抽象化全方位木DPと呼ばれるものを書いているときに、このバグに遭遇しました。計算をするために使用する関数を構造体が要求する仕様にし、木のDFSをラムダ再帰で書いていました。そして、そのライブラリの verify のために問題を解くときに、構造体に std::pair<int,int> を引数に持つ関数を投げたところ、コンパイルで大量のエラーを吐かれました。普段出るようなエラーとは毛色が異なるように見えたので、大学の先輩に相談してコードを小さくしながら原因を特定すると、自分の書いたコードがバグっていたわけではなくて、どうやらGCCがバグっていそうだと分かりました。びっくりです。

verify に使った問題への提出を置いておきます。Codeforces(GNU G++20 11.2.0 (64 bit, winlibs)) や AtCoder(C++ 20 (gcc 12.2)) 上ではこのコードは問題なく動作します。最新のGCC(しかもC++20以降)でしか起こらないバグのようです。

しかしまぁ、バグの原因が複雑すぎです。とはいえ、簡単に踏めそうなバグはとっくに修正されているでしょうから、そういう意味ではかなり小規模なコードでバグを踏んだのはラッキーかもしれません。

とりあえずGCCのバグということになり、バグ報告をしました。自分はこのときは「へぇ〜そんなこともあるんだなぁ」くらいの気持ちでいたのですが、数日後に大学の同期に話をしたところ「宝くじが当たるくらいすごいから記事書いた方がいいw」と言われたので書いています。Zennのアカウントもさっき作りました。今は誇らしさすらありますが、これでGCCから「これはGCCのバグではなくて、単にあなたが悪いです」とか言われちゃったらどうしよう...

バグの内容

  • gcc のバージョン
gcc version 13.2.0 (Homebrew GCC 13.2.0)
  • コンパイルコマンド
g++ -std=c++20
  • コンパイルしたコード
#include <utility>
using P = std::pair<int, int>;
void (*f)(P);

int main(){
    [](auto) {
        P x;
        f(x);
    };
}
  • コンパイル時のエラーメッセージ(抜粋)
'static constexpr bool std::pair<_T1, _T2>::_S_constructible() [with _U1 = int; _U2 = int; _T1 = int; _T2 = int]' used before its definition
  • 再現するために必要だと思われる要素
    • gcc 13.2.0 [1]
    • c++20 以降
    • auto 型の引数を持つ
    • ラムダ式の中で [2]
    • std::pair<hoge,fuga> 型の引数を取る
    • 関数ポインタに
    • 実体を持つその型の変数を投げる [3]

初めてバグ報告をした話

GCC Bugzilla という場所に報告しました

すぐに反応があり、類似のバグを発見した方が言及してくださり、新たなバグレポートが行われたようです。反応を見る限り、確かにGCCのバグだろうと思います。

なにかこれを解決する術を持っていればGCCに貢献できるのかもしれないですが、自分にはそのような力はないので有識者にお任せすることになります。ギリギリ競プロの範疇でも踏む可能性のあるバグなので、解決が待ち遠しいです。

ちなみに、このサイトでは人々のバグレポートや修正に関する統計が取られており、恒常的に貢献している人が何人かいて眺めているだけで面白かったです。

後日談

後日、GCC 12.2.0 以前でもコンパイルに失敗するという報告があり、抽象化全方位木DPのときはそうではなかったので不思議です。コードを小さくしていく過程で別のバグを踏んでしまっている可能性も考えられますが、こちらの調査はまだ手付かずです。

バグ報告の際に注意するべきことを載せた記事を読んでいたのですが、報告するのが億劫になるくらい「それはお前の仕事」「そんなことは報告しなくていい」「その情報がないと調査しようがない」「そんな時間がある人間はウチにはいない」(意訳)などと書かれていて、はじめは報告するか悩みました。しかし、コードを極限まで小さくし、過去の報告を参考に最低限の情報を載せるに留めて、なんとか報告に漕ぎ着けました。気軽に報告するものではなさそうなので、バグを見つけてしまったかもしれないときは、慎重に丁寧に自分側である程度の調査を行う必要があるということです。

おわり

バグをぱくぱく ~(. ________ . )//

脚注
  1. ミニマルな例は GCC 12.2.0 でもコンパイルに失敗します。 ↩︎

  2. 特殊な状況下ではラムダ式ではなくても同様のバグを踏むことがあるようです。詳しくはこちらのバグレポートをご覧ください。 ↩︎

  3. 正しい言い方になっているのかは分かりません。 f(P(0,0)), f(make_pair(0,0)) などと書いたらバグを踏まなくなるという意味です。 ↩︎

Discussion