cs: fix letrec
Report source name when accessing a variable too early, and allow multiple returns (based on continuation capture) for the right-hand side of a `letrec`. The repair directly implements `letrec` as needed in terms of `let` and `set!`, instead of relying on Chez Scheme's `letrec`, unless right-hand sides are simple enough. Implementing `letrec` that way risks losing Chez Scheme optimizations, but schemify takes care of many improvements already.
This commit is contained in:
parent
cea82c1ddc
commit
e37199cd7a
|
@ -226,6 +226,7 @@ RUMBLE_SRCS = rumble/define.ss \
|
||||||
rumble/virtual-register.ss \
|
rumble/virtual-register.ss \
|
||||||
rumble/check.ss \
|
rumble/check.ss \
|
||||||
rumble/syntax-rule.ss \
|
rumble/syntax-rule.ss \
|
||||||
|
rumble/letrec.ss \
|
||||||
rumble/constant.ss \
|
rumble/constant.ss \
|
||||||
rumble/hash-code.ss \
|
rumble/hash-code.ss \
|
||||||
rumble/struct.ss \
|
rumble/struct.ss \
|
||||||
|
|
|
@ -6,11 +6,27 @@
|
||||||
(cond
|
(cond
|
||||||
[(pair? v) (let-values ([(a stripped-a) (correlated->annotation* (car v))]
|
[(pair? v) (let-values ([(a stripped-a) (correlated->annotation* (car v))]
|
||||||
[(d stripped-d) (correlated->annotation* (cdr v))])
|
[(d stripped-d) (correlated->annotation* (cdr v))])
|
||||||
(if (and (eq? a (car v))
|
(cond
|
||||||
|
[(and (eq? a (car v))
|
||||||
(eq? d (cdr v)))
|
(eq? d (cdr v)))
|
||||||
(values v v)
|
(values v v)]
|
||||||
(values (cons a d)
|
[(and (eq? stripped-a 'letrec*)
|
||||||
(cons stripped-a stripped-d))))]
|
(pair? (cdr v)))
|
||||||
|
(let ([names (let loop ([clauses (cadr v)])
|
||||||
|
(cond
|
||||||
|
[(null? clauses) '()]
|
||||||
|
[else
|
||||||
|
(let ([id (caar clauses)])
|
||||||
|
(cons (or (and (correlated? id)
|
||||||
|
(correlated-property id 'undefined-error-name))
|
||||||
|
(if (correlated? id)
|
||||||
|
(correlated-e id)
|
||||||
|
id))
|
||||||
|
(loop (cdr clauses))))]))])
|
||||||
|
(values (list* 'letrec*/names names d)
|
||||||
|
(list* 'letrec*/names names stripped-d)))]
|
||||||
|
[else (values (cons a d)
|
||||||
|
(cons stripped-a stripped-d))]))]
|
||||||
[(correlated? v) (let-values ([(e stripped-e) (correlated->annotation* (correlated-e v))])
|
[(correlated? v) (let-values ([(e stripped-e) (correlated->annotation* (correlated-e v))])
|
||||||
(let ([name (correlated-property v 'inferred-name)])
|
(let ([name (correlated-property v 'inferred-name)])
|
||||||
(define (add-name e)
|
(define (add-name e)
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
begin0
|
begin0
|
||||||
|
|
||||||
|
letrec*/names
|
||||||
|
|
||||||
dynamic-wind
|
dynamic-wind
|
||||||
call-with-current-continuation
|
call-with-current-continuation
|
||||||
call-with-composable-continuation
|
call-with-composable-continuation
|
||||||
|
@ -693,6 +695,7 @@
|
||||||
(include "rumble/define.ss")
|
(include "rumble/define.ss")
|
||||||
(include "rumble/virtual-register.ss")
|
(include "rumble/virtual-register.ss")
|
||||||
(include "rumble/begin0.ss")
|
(include "rumble/begin0.ss")
|
||||||
|
(include "rumble/letrec.ss")
|
||||||
(include "rumble/syntax-rule.ss")
|
(include "rumble/syntax-rule.ss")
|
||||||
(include "rumble/lock.ss")
|
(include "rumble/lock.ss")
|
||||||
(include "rumble/thread-local.ss")
|
(include "rumble/thread-local.ss")
|
||||||
|
|
47
racket/src/cs/rumble/letrec.ss
Normal file
47
racket/src/cs/rumble/letrec.ss
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
(meta define no-early-reference?
|
||||||
|
(lambda (stx ids)
|
||||||
|
(cond
|
||||||
|
[(#%identifier? stx)
|
||||||
|
(not (#%ormap (lambda (id) (free-identifier=? id stx)) ids))]
|
||||||
|
[(let ([d (syntax->datum stx)])
|
||||||
|
(or (number? d) (boolean? d) (string? d) (bytevector? d)))
|
||||||
|
#t]
|
||||||
|
[else
|
||||||
|
(syntax-case stx (quote |#%name| lambda case-lambda)
|
||||||
|
[(quote _) #t]
|
||||||
|
[(|#%name| _ exp) (no-early-reference? #'exp ids)]
|
||||||
|
[(lambda . _) #t]
|
||||||
|
[(case-lambda . _) #t]
|
||||||
|
[_ #f])])))
|
||||||
|
|
||||||
|
(meta define no-early-references?
|
||||||
|
(lambda (rhss ids)
|
||||||
|
(cond
|
||||||
|
[(null? rhss) #t]
|
||||||
|
[else (and (no-early-reference? (car rhss) ids)
|
||||||
|
(no-early-references? (cdr rhss) (cdr ids)))])))
|
||||||
|
|
||||||
|
;; Like `letrec*`, but makes use-before-definition checks explicit so
|
||||||
|
;; that a source name is included in the error messages. Also, the
|
||||||
|
;; expansion allows `call/cc`-based capture and multiple return on the
|
||||||
|
;; right-hand side.
|
||||||
|
(define-syntax (letrec*/names stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ (name ...) ([id rhs] ...) body ...)
|
||||||
|
(cond
|
||||||
|
[(no-early-references? #'(rhs ...) #'(id ...))
|
||||||
|
#'(letrec* ([id rhs] ...) body ...)]
|
||||||
|
[else
|
||||||
|
(with-syntax ([(tmp-id ...) (generate-temporaries #'(id ...))])
|
||||||
|
#'(let ([tmp-id unsafe-undefined] ...)
|
||||||
|
(let-syntax ([id (identifier-syntax
|
||||||
|
[id (check-not-unsafe-undefined tmp-id 'name)]
|
||||||
|
[(set! id exp)
|
||||||
|
(let ([id exp])
|
||||||
|
(check-not-unsafe-undefined/assign tmp-id 'name)
|
||||||
|
(set! tmp-id id))])]
|
||||||
|
...)
|
||||||
|
(set! tmp-id rhs)
|
||||||
|
...
|
||||||
|
(let ()
|
||||||
|
body ...))))])]))
|
Loading…
Reference in New Issue
Block a user