Let's start Scheme

2013-01-21

デバッグしづらいバグ

(多分)マクロ周りの識別子問題なのだが、非常にデバッグしづらいバグの報告を受けた。とりあえず、考えうる限りもっとも小さいと思われる再現コードは以下
(import (rnrs))
(define-syntax foo
  (lambda (x)
    (syntax-case x ()
      ((_)
       (let ()
         ;; こいつが問題。syntax-rulesでも起きるけど
         ;; syntax-caseの方が通常はデバッグが楽なので
         (define-syntax prob
           (lambda (x)
             (syntax-case x ()
               ((_) #'ok))))
         #t)))))
要するにsyntax-caseのテンプレート部分で局所的マクロを定義すると&compileが投げられるという不具合。

エラーのメッセージは局所変数.varが参照されているけど、その定義がIForm上で見つからないというもの。 .varはマクロ展開器がパターン変数等を参照するために自動でつけられるものなのだが、この場合だとfooprobの両方が持っている。

何がこの不具合をデバッグしづらくしているかというと、(デバッガがないというのは置いておいて)probがコンパイル時にコンパイルされるのでどんな感じの中間コードになっているかというのが出力できないこと。コンパイラはいくつかのステップを踏んでVMコードを出力するのだが、このパターンはどのステップで不具合が入るのかとか、なんで入るのかというのを全て推測するしかないのが辛い。

なんとなく推測としてある不具合の原因としては、prob側で参照されている.varfoo側で定義されたものになってるんだろうなぁ、くらいのものである。(恐らく正しいはず)

さて、なんでこんなことが起きるんだ?

わかった、フェーズの問題だ。probはフェーズが違うのに同じ環境を参照しようとしてるからおかしなことになってるんだ。問題はSagittariusはフェーズなんて概念がないので、フェーズ相当の何かしらをどうにかして検出してやる必要がある。

どうでもいいのだが、上記のコードはYpsilonでなぜかout of contextって言われる。まぁ、恐らく僕が面しているものと同じ不具合みたいなものだろう。ついでに、explicit phasingな処理系だと怒られる。runexpandをつけてやる必要がある。

追記の追記:
恐らく、マクロの中で定義された局所マクロのコンパイルをコンパイラが検出できればなんとかなる気がする。ただ、現状ではそんな情報がどこにもない。解決案は2つあって、
  1. コンパイル時にフェーズを暗に持つ
  2. マクロの束縛を検出したらなんらかフラグを立てる
ぶっちゃけ、どちらも変わらないか・・・実装手段が多少違ってくるだけで。

No comments:

Post a Comment