Rustの特長の1つである安全性。そのための大きな役割を担うのが、コンパイラです。このRustコンパイラにコントリビュートしている日本人エンジニアがいるということで、TechFeed公認エキスパートにご就任頂くと同時に、まずは記念のインタビューとしてお話を伺いました。
Rustにコントリビュートすることになったきっかけ
――今日はお時間をとっていただいてありがとうございます。最初に自己紹介をお願いします。
はい、前田喬之です。TaKO8KiというHNで活動していて「タコヤキ」と読みます。インターンを含めて、株式会社マネーフォワードに3年半勤務しています。
Rustは大学3、4年生の頃から使い始めて、しばらくしてコントリビュートするようになり、去年の8月に正式にcompiler-contributorsチームのメンバーになりました。大学では市民工学を専攻していたので特にバックグラウンドは無かったですが、もともとOSS活動に興味があって、最初は自分でライブラリやTUIツールを作ったりしていました。
――日本人でRustのコントリビューターをされている方はそうそういらっしゃらないと思うのですが、どのようなことをされているのですか?
現在は、主に2つあって、まず、RustのDiagnostics working groupに所属して、コンパイルエラー関連のissueに取り組んでいます。Rustはエラー機構 diagnostics(診断)が優秀で、どこを直せばいいのかサジェストを出してくれる場合もあります。活動の中で、新しいエラーを実装したり、エラーにバグがあったら直したりというところをやっています。
次に、generic_const_exprというfeature関連のissueにも少し取り組んでいます。これを使うと、const genericsの型パラメータを定数式に用いることができます。
コンパイラ自身もRustで記述されている
――コンパイラ内部の処理の流れはどのようなものでしょうか。
コンパイラ自身もRustで書かれていて、処理の概要は図1のようになっています。
rustcを実行したタイミングで、まず、rustc_driverというクレートがコマンドライン引数のパースを行い、コンパイラのconfigをその後の過程に渡します。次に、rustc_lexerというクレートでソースコードがトークン列に変換されます。Rustは再帰下降構文解析を用いていて、rustc_parserというクレートがトークン列をAST(Abstruct Syntax Tree 抽象構文木)に変換します。
rustc_parserのソースコード内にはparse_*という名前の付いたメソッドがたくさんあり、これらがパース処理を行います。メソッドは、名前を見たらそのメソッド処理の内容が分かるように命名されてリストされています。たとえば、parseArrayOrRepeatは[3, 3, 3]や[0; 512] のような [ ] に囲まれたものを配列やリピートとしてパースするメソッドです。
マシン語(バイナリ)になるまで
Rustではここから、ASTに変換したものをHIR(High-Level Intermediate Representation)に変換して、型の推論やトレイトの解決、型チェックなどを行います。
次に、HIRはMIR(Middle-Level Intermediate Representation)というものに変換されて、これがborrow checkなどに使われます。この一連のプロセスをlowering(下降)と言います。
このMIRでは、rustc_mir_transformというクレートでコンパイラ最適化を行っています。Rustはコード生成にLLVMを使っているので、その後にMIRをLLVM用の中間表現、LLVM IRに変換してLLVMでマシンコードが生成され、その後他のライブラリやバイナリをリンクしてバイナリが生成されます。バックエンドとしてはLLVMの他にGCCやCraneliftもサポートしています。rustcにはcodegenそのものは実装されていません。
コンパイラのエラー処理とサジェスト
こちらにエラーの例があります。
一行目にエラーの内容が書いてあって、一番下の行を見ると「help:」以下に「このコロンの代わりに -> を使用する」というサジェストがあります。このように分かりやすいメッセージが出ます。このようなエラーは DiagnosticBuilder を直接用いるか、#[derive(Diagnostic)]や#[derive(Subdiagnostic)]といったderiveをstruct・enumに用いることで定義することができます。
こういうものに関しては、必要になるタイミングで我々コントリビューターたちが実装していて、ユーザーが求めているものと違うエラーメッセージが出ている場合やICEがある場合はその都度修正します。
また、最近だとエラーの翻訳に向けてエラー基盤を新しいものに移行する対応が進められていたりします。
- Contribute to the diagnostic translation effort!
- Diagnostic Translation · Issue #100717 · rust-lang/rust
難しいようで始めやすいRust
――現状、Rustについて満足していることは何ですか?
エラーメッセージが親切でとっつきやすいところです。Rustはコンパイラが通るまでは少し大変ですが、通ってしまえば動く状態になって、それまでの道のりは基本的にコンパイラが指摘してくれるので、意外とやりやすいんじゃないかなと思います。
今、業務でRustを使っていますが、個人的には新しいメンバーへの導入もやりやすいように思います。
コンパイラ開発の経験がなくても問題なし!Rustにあなたもコントリビュートしよう!
――Rustで今進められている改善にはどのようなものがあるのでしょうか?
今、ちょうどトレイトシステムのリファクタリングが進められています。 具体的には、新しいトレイトソルバを実装してキャッシュ戦略を見直すことでコンパイル時間を短縮しようというものです。元のトレイトソルバは、InferCtxt、TyCtxtにおけるグローバルキャッシュ、FulfillmentContextという3つの異なるレベルのキャッシュがあり、新しいトレイトソルバでは射影キャッシュが割と重要です。値が確定していない型やリージョンを正規化(canonicalize)して、ソルバ内の推論状態を使用しないので、常にグローバルなキャッシュを使えるようになるようです。
あとは、コンパイラのパフォーマンス改善は継続的に取り組まれています。Compiler performance working groupというのがあって、nnethercoteさんという方が数ヶ月に一回くらい下記のようにブログにまとめています。
――なるほど、ありがとうございます。最後に、新しくコントリビューターになってみたいと思う人にメッセージがありましたら。
自分もそうでしたが、経験がなくてもさわって慣れていくのがよいと思います。基本はドキュメント Rust Compiler Development Guide を読むところからがいいと思います。
最初からRustコンパイラ本体に手をつけるのもいいですが、例えばLinterのrust-clippyなどでしたら取り組みやすいIssueの数が多いので、始めやすいと思います。。自分は最初そうやって取り組んでいきました。また、質問できる場が、分からないところはZulipで聞けば回答をもらえます。
また、「Rust Compiler Ambitions for 2022」という記事の中の「What do I do if I'm interested in compiler development but have no experience in compilers?」という項目で「コンパイラ開発の経験が無くても問題ない」「今いるコントリビューターの多くも、Rustの開発をしながら徐々にコンパイラについて学んだ」という旨のことが書かれていたりするので、あまり気にせずに取り組んでみるのが良いかもしれません。
ソースコードが巨大なのでなかなか全部は難しいですが、部分部分の理解があれば何かしら貢献できるところもあると思うので、ぜひチャレンジしてみてください。
――今日は貴重なお話をありがとうございました。
ありがとうございました。