global変数判定のバグ - Scheme VM を書く
コンパイル時の global 変数判定のバグを発見。
わりと深刻なのでまじめに考えないとまずい。
症状
(compile-lambda '(lambda () (print "Status: 200 OK")) '() '() '() '(HALT)) => (REFER_GLOBAL print (ARGUMENT (CLOSE 1 (CONSTANT "Status: 200 OK" (ARGUMENT (REFER_FREE 0 (SHIFT 1 0 (APPLY 1))))) 0 #f (HALT))))
lambda 内の print 参照が free variable 扱いになり display closure を作ろうとしている。
lambda の外では print は global 扱いなので REFER_GLOBAL して push して free 扱いにするというなぞの挙動に。
何が問題か?
print が free variable と判定されているのが問題。
具体的には find-free で print が free と判定されるのが良くない。
[(symbol? sexp) (cond ((set-member? sexp b) '()) ((set-member? sexp f) (list sexp)) ((set-member? sexp global-variables) '()) (else (list sexp)))]
どれにもあてはまらないときは free variable ではなく global variable と判定するのが良い。
free variable をいかに正しく判定するかという問題に帰着。
おおざっぱな方針
free variable になるのは、親(またはその親)の lambda で引数として渡っているものである。
親からきちんとこれらの引数を引き継いで find-free すれば良いはず。
つまり一番外側の lambda から
(lambda () (lambda (a b) (lambda (c d) ;; (a b) が free 候補 (lambda (e f) ;; (a b c d) が free 候補 ...))))
という感じ。
find-free の引数を思い出す
どの引数も lambda 式の body のコンパイル時に使われる。
e
(vars . free vars) の形式でその lambda 式の中で、引数として扱うもの、見つかった free variable のペア。
f
vars と以前の f の union。
これが free variable と判定されるべき候補。
s
その lambda 内で代入される可能性があるもの。
作業
- e, f, s 見直し => OK
- fが正しく使われているか?
- find-free の symbol 判定がまちがっている?
- 間違ってた
残作業
- stack-vm22.scm 整理
- print 削除
- テストをまとめる
- compile.scm に反映
- stack-vm23
- global 変数の引きまわしいらない疑惑
完了
2.5時間。
テスト書いてなかったら enbug するところだったな。