coq issue #7191: Extraction generates assert false at top level
coq issue #7228: Anomaly with Set Extraction Conservative Types.
いつのまにか CertiCoq が公開されていたので、動かしてみた。 (CertiCoq というのは、証明された Gallina のコンパイラ。 Gallina は Coq の中に入っている依存型で ML っぽい言語)
<URL:https://github.com/PrincetonUniversity/certicoq>
コンパイルはだいたい README.md に書いてあるとおりでいけた。
opam repo add coq-released https://coq.inria.fr/opam/released opam repo add coq-core-dev https://coq.inria.fr/opam/core-dev opam repo add coq-extra-dev https://coq.inria.fr/opam/extra-dev opam switch -A 4.05.0 coq87 eval `opam config env` opam install coq.8.7.1 opam pin add coq 8.7.1 opam install coq-template-coq coq-ext-lib coq-squiggle-eq.dev coq-paramcoq make make install sh make_plugin.sh make install
make_plugin.sh は ちょっと変で、stat -f "%m" file というのコマンドは手元では動かない。 (mtime を調べているっぽいが... そういう stat コマンドがあるかどうかよくわからない -> NetBSD 1.6 発祥の stat にあるようだ。) 適当にいじってどうにかした。
次のステップは plugin/README.md に書いてあり、CertiCoq Compile ref. というコマンドが使えるようになるようだ。
試しに以下のようにしてみる。
From CertiCoq Require Import CertiCoq. Definition nat3 := 3. CertiCoq Compile nat3.
すると、Top.nat3.c というファイルが生成された。 内容は以下のとおり。
struct thread_info; struct thread_info { unsigned int *alloc; unsigned int *limit; struct heap *heap; unsigned int args[1024]; }; extern void body(struct thread_info *tinfo); extern void garbage_collect(unsigned int *, struct thread_info *); extern _Bool is_ptr(unsigned int); unsigned int const body_info_0100111[2] = { 6, 0, }; void body(struct thread_info *tinfo) { unsigned int x110; unsigned int x111; unsigned int kapf_0000111; unsigned int k_1000111; unsigned int *alloc; unsigned int *limit; unsigned int *args; alloc = (*tinfo).alloc; limit = (*tinfo).limit; args = (*tinfo).args; if (!(*body_info_0100111 <= limit - alloc)) { (garbage_collect)(body_info_0100111, tinfo); alloc = (*tinfo).alloc; } x110 = 1U; x111 = (unsigned int) (alloc + 1U); alloc = alloc + 2U; *((unsigned int *) x111 + -1) = 1024U; *((unsigned int *) x111 + 0) = x110; kapf_0000111 = (unsigned int) (alloc + 1U); alloc = alloc + 2U; *((unsigned int *) kapf_0000111 + -1) = 1024U; *((unsigned int *) kapf_0000111 + 0) = x111; k_1000111 = (unsigned int) (alloc + 1U); alloc = alloc + 2U; *((unsigned int *) k_1000111 + -1) = 1024U; *((unsigned int *) k_1000111 + 0) = kapf_0000111; *(args + 1U) = k_1000111; return; }
まぁ、まじめなコンパイラなので、もとのコードは想像しにくい。
これを動かすためのコードを探すと、theories/Runtime にそれっぽいのがあった。 theories/Runtime/Makefile には three というサンプルプログラムを作るルールが書いてあるのだが、 ソースが見当たらない。 なので、必要な runtime だけビルドしてみる。
cd ~/coq/certicoq/theories/Runtime make main.o nogc.o
で、さっき生成した Top.nat3.c のところに戻って、コンパイルしてみる。 (コンパイルオプションは theories/Runtime/Makefile に書いてあったもの)
gcc -m32 -g -O2 ~/coq/certicoq/theories/Runtime/main.o ~/coq/certicoq/theories/Runtime/nogc.o Top.nat3.c
で、実行してみる。
% ./a.out Time taken 0 seconds 0 milliseconds 0(0(0(0)))
まぁ、動いたんですかね。
次は、自然数の 3 じゃなくて、10 を試してみる。
Definition nat10 := 10. CertiCoq Compile nat10.
これで Top.nat10.c が生成され、実行すると以下のようになる。
% ./a.out Time taken 0 seconds 0 milliseconds 0(0(0(0(0(0(0(0(0(0(0))))))))))
まぁ、0 の数がコンストラクタの数なのだろう、たぶん。
3 が 0(0(0(0))) ということは S が 3個と O が 1個。 10 が 0(0(0(0(0(0(0(0(0(0(0)))))))))) ということは S が 10個と O が 1個。
nat も素朴にコンストラクタで構成するわけだな。
では、この前報告した strict evaluation だと無限再帰になるやつを試してみよう。
Definition fstarg (x y : bool) := x. Fixpoint f (z : bool) := fstarg true (f z). Definition g := f true. CertiCoq Compile g.
これで Top.g.c が生成され、コンパイルして実行してみる。
% gcc -m32 -g -O2 ~/coq/certicoq/theories/Runtime/main.o ~/coq/certicoq/theories/Runtime/nogc.o Top.g.c (警告省略) % ./a.out Ran out of heap, and no garbage collector present!
お、GC が必要ですか。
さっきの theories/Runtime で make gc.o として GC を作ってもう一回。
% gcc -m32 -g -O2 ~/coq/certicoq/theories/Runtime/main.o ~/coq/certicoq/theories/Runtime/gc.o Top.g.c % ./a.out Ran out of generations
こんどはしばらく時間がたった後にエラーになった。 まぁこれは CertiCoq の責任じゃない (Coq でどうにかすべき) だろうけど。
Gallina 全体を扱うということは、依存型を扱う必要がある。 proof elimination で消えてしまうものはいいとして、消えないもの、 とくに以下のような、値によって型が変わるものをどう扱うか気になる。 (以下の例では、bool な b の値によって f1 と f2 の返値は bool か nat か変化する)
Definition ft (b : bool) := if b then bool else nat. Definition f1 (b : bool) : ft b := match b return ft b with | true => true | false => 0 end. Definition f2 (b : bool) : ft b := match b return ft b with | true => false | false => 1 end. CertiCoq Compile f1. CertiCoq Compile f2.
というわけで CertiCoq でコンパイルして、f1 と f2 のコンパイル結果の違いを眺めるなどしてみたところ、 たぶん、uniform representation という気がする。 つまり、任意の値は (ポインタかもしれないけど) 1 word で表現できるというやつである。 そういう表現であれば、OCaml の Obj.magic 相当 (あるいは、C の pointer や整数の cast) が 可能であり、依存型であっても動作するコードを生成するのはなんとかなる。
GC (というか、メモリ管理) のところを調べれば確認できるだろ、ということで以下のあたりをながめると やはりそれっぽい。
というか、なんか見覚えがある感じの構造や用語が... おぉ、gc.h の先頭に The current Certicoq code generator uses Ocaml object formats と書いてある。 やはり。
CertiCoq の proof elimination はどうなっているのだろうか。
From CertiCoq Require Import CertiCoq. Definition nat3 := 3. CertiCoq Compile nat3. Definition sig3eq : { n | n = 3 }. Proof. exists 3. reflexivity. Qed. Print sig3eq. (* sig3eq = @exist nat (fun n : nat => @eq nat n (S (S (S O)))) (S (S (S O))) (@eq_refl nat (S (S (S O)))) : @sig nat (fun n : nat => @eq nat n (S (S (S O)))) *) CertiCoq Compile sig3eq. Definition sig3lt : { n | 0 < n }. Proof. exists 3. auto. Qed. Print sig3lt. (* sig3lt = @exist nat (fun n : nat => lt O n) (S (S (S O))) (le_S (S O) (S (S O)) (le_S (S O) (S O) (le_n (S O)))) : @sig nat (fun n : nat => lt O n) *) CertiCoq Compile sig3eq.
で、実行すると以下のような結果が出てきた。
nat3 の 0(0(0(0))) は前にみたとおりで、S (S (S O)) だろう。
extraction だと nat3, sig3eq, sig3lt は同じ結果が出てくるので、 CertiCoq の proof elimination は証明が完全には消えないのだろうな。
sig3eq, sig3lt は sig 型のコンストラクタであるところの exist で作られた値だが、exist の型は以下のとおりで 4引数である。
exist : forall (A : Type) (P : forall _ : A, Prop) (x : A) (_ : P x), @sig A P
だが、実行結果の 0(0(0(0(0))),0(565fdb20,0)) はたぶん 2引数になっている。 A と P は型と命題なのでこれらは消しているのだろう。 そして、残っている最初の引数は 3 なのでで、x だろう。 で、後の引数はおそらく、P x の証明だろう。
生成されたコードをみると、証明の部分に入る値は sig3eq と sig3lt でだいたい同じで、 また、(@eq_refl nat (S (S (S O)))) や (le_S (S O) (S (S O)) (le_S (S O) (S O) (le_n (S O)))) のような複雑な値を作っているようには見えない。 また、なにか自己参照をしている感じで、これはあれだな。 Letouzey2004 で出てきた、let rec f x = f の f だろう。 (実際には let __ = let rec f _ = Obj.repr f in Obj.repr f の __)
つまり、証明の中身は自分自身を返す謎の関数に置き換えるが、 完全に除去することはしないのではないかな。
証明に対応する値が残っていれば、strict evaluation でも変なことはおきないし、 まぁ、そんなものか。
[latest]