Island Life

< DreamhostのVPSを試している。... | 印刷入稿 >

2010/01/06

定数伝搬とリテラルの破壊的変更

Gauche trunkのコンパイラでは定数伝搬を多少真面目に計算するようにしてるんだが、 うっかりリテラルリストを破壊しているようなケースで見える結果が異なるケースが あったのでメモ。

問題のコードは木の集合を構築してゆくような関数で、 木の根の集合を頭にダミーノードをつけたリストに集めていた。 こうしとくと、木の途中のノード (<value> <child> ...) に子供を追加するのも 新たな木の根を追加するのも(push! (cdr node) element)で統一できるのだ。

(define (foo)
  (define roots '(#f)) ; #fはダミーノード
  ... 途中でrootsのcdrが破壊的変更される ...
  (cdr roots)) ; ダミーノードを捨てて木の根のリストを返す

もちろんrootsのリストは変更されるので上のようにリテラルリストを使っては いけない。 でもGaucheはリテラルリストの破壊はチェックしないので、 0.9までだと一回目はちゃんと結果が返っていた。 (二回目以降はrootsの初期値が変更されちゃってるので結果が異なってくる)。

一方、trunkのオプティマイザは、

  • rootsの束縛自体は変更されていない
  • その束縛は定数リストである
  • 定数リストのcdrは定数である

ということから最後の(cdr roots)をコンパイル時に計算してしまうので、 fooは常に '() を返す。

正しい修正は (define roots (list #f)) とすること。

Tag: Gauche

Post a comment

Name: