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]