Opened 5 weeks ago

Last modified 8 hours ago

#1441 new defect

Macro keywords unexpectedly match against imported identifiers

Reported by: sjamaan Owned by: sjamaan
Priority: major Milestone: 4.14.0
Component: expander Version: 4.13.0
Keywords: hygiene, syntax-rules, capture Cc:
Estimated difficulty: hard

Description (last modified by sjamaan)

As reported by Joerg Wittenberger, Al Petrofsky's "unhygienic extraction" of identifiers via syntax-rules keywords list fails when the identifier was imported from another module.

A simplified example:

(module break (break)
  (import scheme)
  (define break "elsewhere"))


(module unhygienic-loop (loop)
  (import scheme)

(define-syntax find-identifier
  (syntax-rules ()
    ((_ ident (x . y) sk fk)
     (find-identifier ident x sk (find-identifier ident y sk fk)))
    ((_ ident #(x ...) sk fk)
     (find-identifier ident (x ...) sk fk))
    ((_ ident form sk fk)
     (let-syntax
         ((check
           (syntax-rules (ident)
             ((_ ident ident* (s-f . s-args) fk_) (s-f ident* . s-args))
             ((_ x y sk_ fk_) fk_))))
       (check form form sk fk)))))


(define-syntax loop
  (syntax-rules ()
    ((_ . exps)
     (let-syntax
         ((l (syntax-rules ()
               ((_ ident exps_)
                (call-with-current-continuation
                 (lambda (ident)
                   (let f ()
                     (begin 'prevent-empty-begin . exps_)
                     (f))))))))
       (find-identifier break exps (l exps) (l bogus exps)))))))


(module foo ()

(import scheme break unhygienic-loop)

;; Uncomment these and comment the above to see the differing behaviour:
;; (import scheme unhygienic-loop)

;; This definition is not required
;; (define break "local")

(display (loop (break 'foo))))

This appears to be incorrect, at least when compared to Chibi on an equivalent set of R7RS modules, it behaves identically in the case where the identifier is defined locally, undefined, or defined by another module.

In CHICKEN 4.12 and earlier, this code behaves the same regardless of where the identifier came from.

Change History (6)

comment:1 Changed 5 weeks ago by sjamaan

  • Description modified (diff)

comment:2 Changed 5 weeks ago by sjamaan

  • Description modified (diff)

comment:3 Changed 7 days ago by sjamaan

  • Milestone changed from someday to 4.14.0

comment:4 Changed 9 hours ago by sjamaan

An interesting testcase where I believe the new version is behaving correctly but 4.12.0 behaving differently:

(module match-it (match-foo)
 (import scheme)
    
 (define-syntax match-foo
   (syntax-rules ()
     ((_)
      (let-syntax ((match-identifier
                    (syntax-rules (foo)
                      ((_ foo) #t)
                      ((_ ?whatever) #f))))
        (match-identifier foo))))))

(module myfoo (foo)
  (import scheme)
  (define foo 1))

(import match-it)
(print (match-foo))

(import myfoo)
(print (match-foo))

I would expect both to print #t like in 4.13.0

comment:5 Changed 8 hours ago by sjamaan

Simplified case:

(module undef (undefined)
  (import scheme)
  (define undefined 1))

(module expando (do-it)
  (import scheme)
  (define-syntax do-it
    (syntax-rules ()
      ((_)
       (let-syntax ((final
                     (syntax-rules ()
                       ((_ ?x) ?x))))
         (final undefined))))))

(import undef expando)
;; Should fail, not print 1, because the let-syntax expansion should rename introduced 
;; identifiers from its SE (but input should be looked up here)
(print (do-it))

comment:6 Changed 8 hours ago by sjamaan

OK, this simplified case is caused by ##sys#alias-global-hook, so perhaps we should ignore that for now (see #1131).

Note: See TracTickets for help on using tickets.