補助構文のrename

(import (prefix (rnrs) rnrs.))
(rnrs.define-record-type (foo make-foo foo?)
  (rnrs.fields (rnrs.mutable brr)))
(make-foo 1)

のようなコードが正常に動作しないというバグ(rnrs.fieldsが正常にrenameされないように振る舞い、fieldsを代わりに使うことで動作する)。原因自体は非常に単純で:

  • boot/runtimes/srfi-mosh/lib.rnrs/rnrs/records/syntactic.ss
      ; Searches for a clause beginning with the given symbol,
      ; returning the entire clause (as a syntax object) if found
      ; or #f if no such clause is found.

      (define (clauses-assq sym clauses)
        (syntax-case clauses ()
         (((x1 x2 ...) y ...)
          (if (and (identifier? #'x1)
                   (eq? sym (syntax->datum #'x1)))
              #'(x1 x2 ...)
              (clauses-assq sym #'(y ...))))
         ((y0 y1 y2 ...)
          (clauses-assq sym #'(y1 y2 ...)))
         (x
          #f)))
...
               (fields-clause (clauses-assq 'fields clauses))

Larcenyから移植してきたR6RS recordの実装が、syntax→datumしたうえでeq?でシンボルと比較している部分があるため。clauses-assqは、syntax-caseな手続きの内部で、clauseを収集するために使用される。eq?で比較すると構文情報が抜け落ちてしまうため、renameが効かなくなる。本来はfree-identifier=?的に比較する必要がある。
例えば、

(library (check)
         (export check
                 foo)
         (import (rnrs))
(define-syntax foo (lambda (p) 'invalid))
(define-syntax check
  (lambda (p)
    (define (proc x)
       (display (list
                  'eq?: (eq? 'foo (syntax->datum x))
                  'id?: (free-identifier=?
                          #'foo
                          x)))
       (newline)
       #t)
    (syntax-case p ()
      ((_ sym)
       (proc #'sym)
       (syntax "Do nothing"))))))

(import (check)(rnrs))

(check foo)

のようなコードを書いて、(check)ライブラリからfooをexportしたりしなかったりすることでこれらの挙動を観察できる。