thrustのバグを見つけて直した話

thrustがasync対応していたのを先月見つけた。

in-neuro.hatenablog.com

ドキュメントがまるで書かれていないところをコードを読み進めて試していたのだが、そこで不可解な動作を発見した。

以下のようなコードを書いたとする。thrustの関数を非同期に読んでそのfutureを返す関数だ。

#include <thrust/future.h>
#include <iostream>

// the definition is in a .cu file and will be linked later
thrust::system::cuda::unique_eager_future<int> do_something();

int main()
{
    auto f = do_something();
    std::cout << f.get() << std::endl;
    return 0;
}

これはコンパイルエラーになる。

$ g++-8 -std=c++11 -O2 -c main.cpp -I/usr/local/cuda/include/
In file included from /usr/local/cuda/include/thrust/system/cuda/detail/future.inl:26,
                 from /usr/local/cuda/include/thrust/system/cuda/future.h:71,
                 from /usr/local/cuda/include/thrust/future.h:55,
                 from main.cpp:1:
/usr/local/cuda/include/thrust/detail/event_error.h:110:19: error: explicit specialization of ‘template<class T> struct thrust::system::is_error_code_enum’ outside its namespace must use a nested-name-specifier [-fpermissive]
 template<> struct is_error_code_enum<event_errc> : true_type {};
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

これは単純な話で、is_error_code_enumというトレイト構造体をevent_errcという型に対して特殊化しているのだが、その時にnamespaceを間違えていたのだ。構造体が所属する名前空間の外でその構造体を特殊化する時は、nested-name-specifierを明示する必要があり、今のthrustのバージョンではそれをしていなかった。

不思議なことに、これは翻訳単位を分けない限り気がつかない。同じファイルに関数の実体まで書いて完結させると問題なく通ってしまう。コンパイラの最適化周りの複雑な何かが絡んでいるのか規格通りの挙動なのかはよくわからないが、とにかくそうなってしまうので発見しづらい。テストコードはおそらく全て1ファイルで完結してしまっているのだろう。

というわけで、名前空間を補うだけのPull Reqを送った。サンプルコードも症状もバージョンも全部書いて、かつ問題点と解決策が自明なので一瞬でマージされるだろうと思ったら実際には1ヶ月近くかかった。

github.com

とはいえマージされたので、thrustのコントリビュータ一覧に名前が載った。小さなことではあるがそれなりに嬉しい。