defer require and provide expansion to module body

Closes PR 14936
This commit is contained in:
Matthew Flatt 2015-01-18 11:33:47 -07:00
parent c6802ed107
commit 2d4b4527c0
2 changed files with 185 additions and 171 deletions

View File

@ -1294,6 +1294,12 @@
(dynamic-require tmx 'x))) (dynamic-require tmx 'x)))
(delete-file zo-path)) (delete-file zo-path))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Check that `provide` doesn't run enclosed expanders until within a
;; module (as opposed to a `#%module-begin` expansion):
(module check-contract-out-by-itself racket (provide (contract-out)))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(report-errs) (report-errs)

View File

@ -241,161 +241,166 @@
;; require ;; require
(define-syntax (require stx) (define-syntax (require stx)
(unless (memq (syntax-local-context) '(module module-begin top-level)) (case (syntax-local-context)
(raise-syntax-error #f [(module-begin)
"not at module level or top level" (quasisyntax/loc stx (begin #,stx))]
stx)) [(module top-level)
(parameterize ([current-require-module-path #f]) (parameterize ([current-require-module-path #f])
(letrec ([mode-wrap (letrec ([mode-wrap
(lambda (mode base) (lambda (mode base)
(cond (cond
[(eq? mode 0) base] [(eq? mode 0) base]
[else #`(for-meta #,mode #,base)]))] [else #`(for-meta #,mode #,base)]))]
[simple-path? (lambda (p) [simple-path? (lambda (p)
(syntax-case p (lib quote) (syntax-case p (lib quote)
[(lib . _) [(lib . _)
(check-lib-form p)] (check-lib-form p)]
[(quote . _) [(quote . _)
(check-lib-form p)] (check-lib-form p)]
[_ [_
(or (identifier? p) (or (identifier? p)
(and (string? (syntax-e p)) (and (string? (syntax-e p))
(module-path? (syntax-e p))))]))] (module-path? (syntax-e p))))]))]
[transform-simple [transform-simple
(lambda (in base-mode) (lambda (in base-mode)
(syntax-case in (lib file planet submod prefix-in except-in quote) (syntax-case in (lib file planet submod prefix-in except-in quote)
;; Detect simple cases first: ;; Detect simple cases first:
[_ [_
(string? (syntax-e in)) (string? (syntax-e in))
(begin (begin
(unless (module-path? (syntax-e in)) (unless (module-path? (syntax-e in))
(raise-syntax-error (raise-syntax-error
#f #f
"bad module-path string" "bad module-path string"
stx stx
in)) in))
(list (mode-wrap base-mode in)))] (list (mode-wrap base-mode in)))]
[_ [_
(and (identifier? in) (and (identifier? in)
(module-path? (syntax-e #'in))) (module-path? (syntax-e #'in)))
(list (mode-wrap base-mode in))] (list (mode-wrap base-mode in))]
[(quote . s) [(quote . s)
(check-lib-form in) (check-lib-form in)
(list (mode-wrap base-mode (xlate-path in)))] (list (mode-wrap base-mode (xlate-path in)))]
[(lib . s) [(lib . s)
(check-lib-form in) (check-lib-form in)
(list (mode-wrap base-mode (xlate-path in)))] (list (mode-wrap base-mode (xlate-path in)))]
[(file . s) [(file . s)
(check-lib-form in) (check-lib-form in)
(list (mode-wrap base-mode (xlate-path in)))] (list (mode-wrap base-mode (xlate-path in)))]
[(planet . s) [(planet . s)
(check-lib-form in) (check-lib-form in)
(list (mode-wrap base-mode (xlate-path in)))] (list (mode-wrap base-mode (xlate-path in)))]
[(submod . s) [(submod . s)
(check-lib-form in) (check-lib-form in)
(list (mode-wrap base-mode (xlate-path in)))] (list (mode-wrap base-mode (xlate-path in)))]
[(prefix-in pfx path) [(prefix-in pfx path)
(simple-path? #'path) (simple-path? #'path)
(list (mode-wrap (list (mode-wrap
base-mode base-mode
(datum->syntax (datum->syntax
#'path #'path
(syntax-e (syntax-e
(quasisyntax (quasisyntax
(prefix pfx #,(xlate-path #'path)))) (prefix pfx #,(xlate-path #'path))))
in in
in)))] in)))]
[(except-in path id ...) [(except-in path id ...)
(and (simple-path? #'path) (and (simple-path? #'path)
;; check that it's well-formed... ;; check that it's well-formed...
(call-with-values (lambda () (expand-import in)) (call-with-values (lambda () (expand-import in))
(lambda (a b) #t))) (lambda (a b) #t)))
(list (mode-wrap (list (mode-wrap
base-mode base-mode
(datum->syntax (datum->syntax
#'path #'path
(syntax-e (syntax-e
(quasisyntax/loc in (quasisyntax/loc in
(all-except #,(xlate-path #'path) id ...))))))] (all-except #,(xlate-path #'path) id ...))))))]
;; General case: ;; General case:
[_ (let-values ([(imports sources) (expand-import in)]) [_ (let-values ([(imports sources) (expand-import in)])
;; TODO: collapse back to simple cases when possible ;; TODO: collapse back to simple cases when possible
(append (append
(map (lambda (import) (map (lambda (import)
#`(just-meta #`(just-meta
#,(import-orig-mode import) #,(import-orig-mode import)
#,(mode-wrap (phase+ base-mode (import-req-mode import)) #,(mode-wrap (phase+ base-mode (import-req-mode import))
#`(rename #,(import-src-mod-path import) #`(rename #,(import-src-mod-path import)
#,(import-local-id import) #,(import-local-id import)
#,(import-src-sym import))))) #,(import-src-sym import)))))
imports) imports)
(map (lambda (src) (map (lambda (src)
(mode-wrap (phase+ base-mode (import-source-mode src)) (mode-wrap (phase+ base-mode (import-source-mode src))
#`(only #,(import-source-mod-path-stx src)))) #`(only #,(import-source-mod-path-stx src))))
sources)))]))] sources)))]))]
[transform-one [transform-one
(lambda (in) (lambda (in)
;; Recognize `for-syntax', etc. for simple cases: ;; Recognize `for-syntax', etc. for simple cases:
(syntax-case in (for-meta) (syntax-case in (for-meta)
[(for-meta n elem ...) [(for-meta n elem ...)
(or (exact-integer? (syntax-e #'n)) (or (exact-integer? (syntax-e #'n))
(not (syntax-e #'n))) (not (syntax-e #'n)))
(apply append (apply append
(map (lambda (in) (map (lambda (in)
(transform-simple in (syntax-e #'n))) (transform-simple in (syntax-e #'n)))
(syntax->list #'(elem ...))))] (syntax->list #'(elem ...))))]
[(for-something elem ...) [(for-something elem ...)
(and (identifier? #'for-something) (and (identifier? #'for-something)
(ormap (lambda (i) (free-identifier=? i #'for-something)) (ormap (lambda (i) (free-identifier=? i #'for-something))
(list #'for-syntax #'for-template #'for-label))) (list #'for-syntax #'for-template #'for-label)))
(apply append (apply append
(map (lambda (in) (map (lambda (in)
(transform-simple in (transform-simple in
(cond (cond
[(free-identifier=? #'for-something #'for-syntax) [(free-identifier=? #'for-something #'for-syntax)
1] 1]
[(free-identifier=? #'for-something #'for-template) [(free-identifier=? #'for-something #'for-template)
-1] -1]
[(free-identifier=? #'for-something #'for-label) [(free-identifier=? #'for-something #'for-label)
#f]))) #f])))
(syntax->list #'(elem ...))))] (syntax->list #'(elem ...))))]
[_ (transform-simple in 0 #| run phase |#)]))]) [_ (transform-simple in 0 #| run phase |#)]))])
(syntax-case stx () (syntax-case stx ()
[(_ in) [(_ in)
(with-syntax ([(new-in ...) (transform-one #'in)]) (with-syntax ([(new-in ...) (transform-one #'in)])
(syntax/loc stx (syntax/loc stx
(#%require new-in ...)))] (#%require new-in ...)))]
[(_ in ...) [(_ in ...)
;; Prefetch on simple module paths: ;; Prefetch on simple module paths:
(let ([prefetches (let ([prefetches
(let loop ([in (syntax->list #'(in ...))]) (let loop ([in (syntax->list #'(in ...))])
(cond (cond
[(null? in) null] [(null? in) null]
[(let ([a (syntax->datum (car in))]) [(let ([a (syntax->datum (car in))])
(and (module-path? a) a)) (and (module-path? a) a))
=> (lambda (a) => (lambda (a)
(cons a (loop (cdr in))))] (cons a (loop (cdr in))))]
[else [else
(let ([a (syntax->list (car in))]) (let ([a (syntax->list (car in))])
(if (and a (if (and a
(let ([i (car a)]) (let ([i (car a)])
(and (identifier? i) (and (identifier? i)
(or (free-identifier=? #'for-something #'for-syntax) (or (free-identifier=? #'for-something #'for-syntax)
(free-identifier=? #'for-something #'for-template) (free-identifier=? #'for-something #'for-template)
(free-identifier=? #'for-something #'for-label))))) (free-identifier=? #'for-something #'for-label)))))
(loop (append (cdr a) (cdr in))) (loop (append (cdr a) (cdr in)))
(loop (cdr in))))]))]) (loop (cdr in))))]))])
(unless (null? prefetches) (unless (null? prefetches)
(log-message (current-logger) (log-message (current-logger)
'info 'info
'module-prefetch 'module-prefetch
(format "module-prefetch: ~s in: ~s" (format "module-prefetch: ~s in: ~s"
prefetches prefetches
(current-load-relative-directory)) (current-load-relative-directory))
(list prefetches (current-load-relative-directory)) (list prefetches (current-load-relative-directory))
#f)) #f))
(syntax/loc stx (syntax/loc stx
(begin (require in) ...)))])))) (begin (require in) ...)))])))]
[else
(raise-syntax-error #f
"not at module level or top level"
stx)]))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; require transformers ;; require transformers
@ -672,22 +677,25 @@
;; provide ;; provide
(define-syntax (provide stx) (define-syntax (provide stx)
(unless (memq (syntax-local-context) '(module module-begin)) (case (syntax-local-context)
(raise-syntax-error #f [(module-begin)
"not at module level" (quasisyntax/loc stx (begin #,stx))]
stx)) [(module)
(syntax-case stx ()
(syntax-case stx () [(_ out ...)
[(_ out ...) (with-syntax ([(out ...)
(with-syntax ([(out ...) (map (lambda (o) (pre-expand-export o null))
(map (lambda (o) (pre-expand-export o null)) (syntax->list #'(out ...)))])
(syntax->list #'(out ...)))]) (syntax-property
(syntax-property (quasisyntax/loc stx
(quasisyntax/loc stx (#%provide #,(syntax-property
(#%provide #,(syntax-property #`(expand (provide-trampoline out ...))
#`(expand (provide-trampoline out ...)) 'certify-mode 'transparent)))
'certify-mode 'transparent))) 'certify-mode 'transparent))])]
'certify-mode 'transparent))])) [else
(raise-syntax-error #f
"not at module level"
stx)]))
(define-syntax (provide-trampoline stx) (define-syntax (provide-trampoline stx)
(syntax-case stx () (syntax-case stx ()