Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Rustを支えるインタープリター ~miriとChalkと、宣言マクロ~ 2018/08/01 Rust LT #2 原 将己 (ウォンテッドリー株式会社)
Slide 2
Slide 2 text
miri: MIRインタプリタ
Slide 3
Slide 3 text
miriを使う (1) • https://github.com/solson/miri • 手順 1. nightly toolchainとxargoを入手 2. miriをビルド 3. MIRつきのstdを作成 4. miriでお好きなRustコードが動かせます!
Slide 4
Slide 4 text
miriを使う (2) • https://play.rust-lang.org/ • constと書く
Slide 5
Slide 5 text
miri • MIR Interpreter • MIRを “Rust abstract machine” の上で実行する • unsafeを含め、ほとんどのRustコードを実行できる
Slide 6
Slide 6 text
HIR (High-level Intermediate Representation) • ASTを名前解決して脱糖したもの
Slide 7
Slide 7 text
MIR (Mid-level Intermediate Representation) • LLVM IRを生成する直前の中間表現 • 多相で、制御フローグラフだがSSAではない
Slide 8
Slide 8 text
Rust Abstract Machine • メモリ = バイト列 • ターゲットアーキテクチャに依存する • アラインメント • エンディアン • usizeの大きさ • 完全な機械ではないが、典型的なunsafeコードを実行できる
Slide 9
Slide 9 text
Constクイズ • 次のうちnightly compilerでconstに代入できるのはどれ? drop(42) if size_of::() == 4 { "32" } else { "64" } *&42 Vec::::new()
Slide 10
Slide 10 text
Constクイズ • 次のうちnightly compilerでconstに代入できるのはどれ? drop(42) if size_of::() == 4 { "32" } else { "64" } *&42 Vec::::new()
Slide 11
Slide 11 text
CTFE (Compile-Time Function Evaluation) • C++でいうconstexpr • ~2017: librustc_const_eval (HIRインタプリタ) • 2018~: miri (MIRインタプリタ)
Slide 12
Slide 12 text
CTFEの歴史 • 有史以前: staticの中身はLLVM定数式で実現されていた • 2011年12月 定数パターンのためにeval_const_exprが爆誕。 これはAST(今のHIR)を直接評価していた。 (64ce092) • 2013年03月 配列の長さに定数式が利用可能になる (#5112) • 2016年03月 rustc_const_evalが切り出される(#32259) • 2016年08月 最初期のstatic生成コードが削除される (#35764) • 2017年12月 miriがコンパイラに統合開始 (#45002) • 2018年3月 rustc_const_evalが削除、miriが正式稼動 (#46882) • 2018年5月 miriがstableで利用開始 (1.26.0)
Slide 13
Slide 13 text
miriとCTFE • miriになったことで参照が使えるようになった • 配列のインデックス(Index trait)ですら参照を使っている • miriには可能でもCTFEでは禁止される操作がある • 分岐とループ • dropの呼び出し • 参照のアドレス取り出し etc. • 非決定的な操作を許可すると未定義動作になりうる • 配列サイズが場所によって異なる値に評価される、など
Slide 14
Slide 14 text
Chalk: トレイトロジックソルバー
Slide 15
Slide 15 text
chalkを使う • https://github.com/rust-lang-nursery/chalk • 手順 1. cargo run (要nightly) 2. REPLが起動する 3. → $ cargo run ?- load libstd.chalk ?- i32: Clone ?- ^C
Slide 16
Slide 16 text
chalkとは • トレイト解決のためのProlog風ソルバー • Prolog = 手続き型 + 単一化 + バックトラック • chalk = Prolog – 副作用 + 量化 + 様相
Slide 17
Slide 17 text
Prologの復習: 項 • 右のように「関数のようなもの」を ネストさせ、項 (term) を作る。 • 単射で互いに交わらないので、関数 というよりもRustのバリアントに近 い。 • 変数を含めることができる(Prolog では大文字して区別する) f(g(a), f(b)) f(a) != f(b) f(a) != g(b) f(X, g(X))
Slide 18
Slide 18 text
Prologの復習: 述語と節 • 述語 (predicate) は項と似ているが、 真偽を表わす • Prologプログラムは節 (clause) の 集まりである。節はパターンマッチ のように動作する。 • ゴール節を書くことで実行開始する p(g(a), f(b)) p(X, f(Y)) :- q(X), q(Y) ?- p(f(a), X)
Slide 19
Slide 19 text
Prologの復習: 単一化とバックトラック • 通常のパターンマッチと異なり、右 辺の変数は重複してよい。これは単 一化 (unification) によって処理さ れる。 • 複数の節にマッチするときは、バッ クトラックにより各選択肢を順番に 試す。 p(a, f(X)) :- true P(g(X), Y) :- p(X, Y) eq(X, X) :- true
Slide 20
Slide 20 text
Prologの復習: 例 • リストを連結する関数→「ZはXとYを連結したリスト」という 述語として表現 append(nil, X, X) :- true append(cons(X, Y), Z, cons(X, W)) :- append(Y, Z, W)
Slide 21
Slide 21 text
Rustのトレイトシステムとの対応 • Rustの型→Prologの項 • Rustのトレイト→Prologの述語 • Rustのtrait impl→Prologの確定節 • Rustのtrait制約→Prologのゴール節 • 多相性→??? • サブタイピング→??? • 射影型→??? • クレート際モード→??? • 否定的な推論→???
Slide 22
Slide 22 text
ChalkをPrologのように使う • Prologと異なり、関数記号はあらかじめ宣言する必要がある struct Nil {} struct Cons {} struct i32 {}
Slide 23
Slide 23 text
ChalkをPrologのように使う • 述語の第一引数 (Self) の位置が特殊 // 第一引数は書かない trait Append {} // 第一引数がforの後にくる impl Append for X {}
Slide 24
Slide 24 text
ChalkをPrologのように使う • 練習: Consの場合に対応させて、以下のクエリを通す $ cargo run ?- load metalist.chalk ?- exists { Cons: Append, X> } Unique; substitution [?0 := Cons>], lifetime constraints []
Slide 25
Slide 25 text
曖昧性 • 解が複数ある場合は失敗する ?- load libstd.chalk ?- exists { T: AsRef } Unique ?- exists { T: AsRef> } Ambiguous
Slide 26
Slide 26 text
多相性 • forall量化とexists量化をネストする ?- load libstd.chalk ?- forall { if(T: Sized) { exists { U: AsRef } } } Unique
Slide 27
Slide 27 text
射影型 • 射影型の正規化には制限がある ?- load iter.chalk ?- exists { as Iterator>::Item = T } Ambiguous ?- exists { Normalize( as Iterator>::Item -> T) } Unique 練習: 上のように動作するiter.chalkを書く
Slide 28
Slide 28 text
否定 • 強い否定を記述できる (impl !Trait for T) ?- exists { (T: Sized, T: AsRef>) } Unique 練習: 上のようになるようにlibstd.chalkを修正せよ
Slide 29
Slide 29 text
クレート際モード • 以下のような例を考える trait Foo {} trait Bar {} struct A {} struct B {} impl Foo for A {} impl Bar for B {}
Slide 30
Slide 30 text
クレート際モード • compat {} という様相を用いると、全ての可能世界での可能性 になる……なってほしい ?- exists { T: Foo } Unique ?- exists { (T: Foo, T: Bar) } No possible solution. ?- compatible { exists { (T: Foo, T: Bar) } } Ambiguous ←現状はNo possible solutionが出力される
Slide 31
Slide 31 text
Chalkまとめ • Chalkはトレイト解決用のProlog風言語 • コンパイラの責務が分割されることで、高速化・バグ修正・新 機能の追加が容易になることが期待されている • 現在Rustコンパイラへの統合が進められつつある • REPLで試すこともできるが、まだ挙動がところどころ怪しい
Slide 32
Slide 32 text
まとめ • Rustコンパイラ内にはインタプリタが棲んでいる • miri: constのコンパイル時計算のため • chalk: トレイト解決のため • 宣言マクロインタプリタ: マクロのため • つまり、メタプログラミングをすることができる。