デバッグ続き - Scheme VM を書く
ふぅ。最小の再現状況を作るべく、数百行の手続きから以下のコードに絞り込まれた。
こいつを VM で実行すると実行の順序がおかしくなる。
これ以上はどうも削れないので(削ると再現しなくなる)、命令列を直接見て机上デバッグしよう。
(define (b iform) (vector-ref (vector-ref (vector-ref iform 1) 0) 0) (vector-set! (vector-ref iform 1) 0 '())) (define (a) (and (b '#(1 #(#(#t))))) (print "done")) (a)
そういえば以前からデバッグにはまると、プログラミング言語に依存しないデバッグ技術の文書を探して見つからないというループを繰り返すなぁ。
二分法とか引数の固定とか依存関係の削除とかそういうノウハウまとまった場所ないかな。
追記
原因判明。
- LOCAL_JMP のオフセット計算間違い
- スタック壊れる
- tail call optimize のバグ
などを想像していましたが、全然違う。
and を if に展開するコードが原因。
(define (and->if sexp) (if (null? (cdr sexp)) #t (let* ([args (cdr sexp)] [last (list-ref args (- (length args) 1))]) (fold-right (lambda (a b) `(if ,a ,b #f)) last args))))
(and->if '(and (dangerous-proc))) => (if (dangerous-proc) (dangerous-proc) #f)
小学生のような間違いをしてしまいました。dangerous-proc に副作用がある場合に死にますね。
以前も C のマクロで同じミスを・・・。
このバグを突き止める力と、このバグを埋めてしまう力のなさが矛盾している気がする。
追記2
or も同様におかしかったので修正。
副作用がある場合のテストケースを追加。
これにて一件落着。