- Cpp2Rustはclang ASTベースの構文主導トランスレータで、C++入力を完全に安全なRustコードへ自動変換する
- 翻訳プロセスでは、clangでC++ファイルをパースしてASTを作成し、ASTを巡回しながらRustコードを文字列として生成した後、
rustfmtで単一の.rsファイルとして出力する
- デフォルトは参照カウントモデルで、デバッグと性能比較のために
--model=unsafeでunsafe Rustの生成も可能
- 生成されたコードは
libcc2rsランタイムライブラリに依存し、Cポインタはnull、算術演算、エイリアスをモデル化するPtr<T>へ変換される
- プログラム全体の翻訳には
compile_commands.jsonが必要で、CMakeプロジェクトではCMAKE_EXPORT_COMPILE_COMMANDS=ONフラグで生成できる
C++から安全なRustへの自動変換
翻訳パイプライン
- 入力C++ファイルをまずclangでパースし、ASTを生成する
- その後ASTを巡回しながらRustコードを文字列として出力する
- 必要に応じて
libcc2rsランタイムライブラリ呼び出しを挿入する
- 例としてはraw pointer semanticsの処理がある
- 最後に
rustfmtを使ってRustコードを単一の.rsファイルに整形する
安全なRustとunsafe Rustモデル
- デフォルト動作では参照カウントモデルを使用し、完全に安全なRust出力を生成する
- unsafe Rustジェネレータも提供される
- コマンドライン引数は
--model=unsafe
- 用途はデバッグと性能比較
libcc2rsランタイムライブラリ
- 生成されたコードは、翻訳プロセスを単純化するために設計されたランタイムライブラリに依存する
- Cポインタは
libcc2rsが提供するPtr<T>型へ変換される
Ptr<T>はCポインタの意味論をモデル化する
- Rustのborrow checkerは検査済みランタイム操作を通じて満たす
ビルドと実行フロー
- Ubuntuの依存関係インストール例には
libclang-22-dev、clang++-22、ninja-build、cmake、ruffが含まれる
- ビルドは
cmake -GNinja ..、ninja、ninja checkの順に進める
- 単一ファイル翻訳コマンドは次の形式
./build/cpp2rust/cpp2rust --file=<file>.cpp -o=<file>.rs
- unsafe Rustを生成するには次のように実行する
./build/cpp2rust/cpp2rust --file=<file>.cpp -o=<file>.rs --model=unsafe
最小例とプログラム全体の翻訳
hello.cppのprintf("hello world\n")の例は、Rustのprintln!("hello world")を含むコードへ変換される
- 変換された
hello.rsは次のようにコンパイルして実行する
rustc hello.rs -L ../libcc2rs/target/debug
./hello
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
./build/cpp2rust/cpp2rust --dir=<dir> -o <output>.rs
<dir>はcompile_commands.jsonが入っているディレクトリでなければならない
テストコマンド
- 全テストは
ninja checkで実行する
- ユニットテストは
ninja check-unitで実行する
libcc2rsのユニットテストはninja check-libcc2rsで実行する
libcc2rs-macrosのユニットテストはninja check-libcc2rs-macrosで実行する
- 意図的に変更した後、期待出力を再生成するには
REPLACE_EXPECTED=1 ninja check-unitを使用する
1件のコメント
Lobste.rsの意見
今年2月にDan Wallachの発表を聞いたが、DARPAの**TRACTOR(Translating All C to Rust)**プロジェクトを紹介していた [1] [2] [3]
研究プログラムの試験・評価はMIT Lincoln Laboratoryのチームが担当しているとのこと
まだ研究段階だと理解しているが、このテーマにはとても関心があり、こうしたツールやアイデアが今後どう発展するのかかなり気になる
メモリ管理に起因する多くの「些細な」バグを実際になくせるかもしれない
[1] https://www.darpa.mil/research/programs/translating-all-c-to-rust
[2] https://github.com/DARPA-TRACTOR-Program
[3] https://ll.mit.edu/r-d/projects/…
興味深い。著者らがこれで変換し、自分たちでも面白いと考えているライブラリやアプリのデモを1つか2つ見られたら本当にうれしい
たとえば https://github.com/Cpp2Rust/cpp2rust-testsuite/… を見ると、安全なRustではあるかもしれないが、慣用的なRustだと言うのは明らかに難しい
なぜ変換ステップが必要なのかよくわからない。メモリ安全なRustへ変換できるだけの十分な情報があるなら、C++についても静的解析で安全だと保証できるだけの情報があるのではないか?
広く配布されているソフトウェアのセキュリティ脆弱性の約70%は、CやC++のような言語におけるメモリ安全性バグに由来し、静的解析・サニタイザー・ハードウェア隔離といった緩和策に数十年投資してきたにもかかわらず、攻撃者は依然として安全でないメモリ操作を悪用しているという
長期的に有望な解決策は既存のC++コードベースをRustのようなメモリ安全な言語へ移行することだが、手作業ではコストが高すぎ、しかもエラーが起きやすいと説明している
Cpp2Rustは、C++プログラムを機能的に等価でメモリ安全なRustコードへ自動変換できる最初のシステムだと主張している
パフォーマンスの一部をセキュリティと引き換えにしつつ、C++の無制限なエイリアス参照とRustの所有権モデルの根本的な不一致を、ランタイムの所有権・可変性チェックの挿入によって解決し、意味を保ったまま安全性を保証するという
動的検査のオーバーヘッドを減らすためにRustコード向けのソース間最適化も作成しており、冗長な所有権操作を取り除くことで失われた性能のかなりの部分を回復するとしている
したがって動機は単に安全性を証明することではなく、Rustへ移植し、その後の開発もRustへ移しやすくすることにある
この種のプロジェクトの発想は、より慣用的なRustへさらにリファクタリングするための出発点を作ることであり、そうなればコードを解析可能にできる
できれば変換後のコードからランタイム検査やunsafeな部分も取り除けるようになる
C++を「Rustっぽい形」にリファクタリングして同じように解析可能にすればよいのでは、と言えるかもしれないが、C++はより多くの柔軟性を許す異なる意味論を持つため、解析すべき経路が指数的に増えたり、静的解析が曖昧な行き止まりに陥ったりしうる
静的解析器に難しいことをさせず、境界ケースを悪用しないと約束する必要があり、そのためにはより制約の強い意味論と追加アノテーションを備えたC++方言を作ることになる
Circle/Safe C++は可能性を示したが、C++ WGがその方向を強く拒否したため、サポートされないRust風C++方言を自分で使うか、単にRustを使うかしか残らない
入力コード自体が安全でない場合には、正確にはどう動作するのだろうか?