From 3b031d6c876620c16ad86ef7e22048d05c30f8bd Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Sun, 7 Aug 2011 09:52:22 -0600 Subject: [PATCH] `define' + keyword lambda at top/module level => static checking When `define' binds a keyword procedure at the top level or module level, then it actually binds syntax that checks (i.e., warns if arith or keywords are bad) and optimizes (i.e., avoids checking keywords or allocating keyword-argument lists) first-order uses of the procedure. Checking and conversion are currently only performed for top- and module-level bindings, because some macros detect the difference between a variable or syntax definition --- notably `class' and `unit'. A related problem in `class' is that the expansion produces multiple definitions (for direct access to the fast version of the function), which `class' turns into fields instead of methods. --- collects/racket/private/kw.rkt | 563 ++++++++++++++++++++++----------- 1 file changed, 375 insertions(+), 188 deletions(-) diff --git a/collects/racket/private/kw.rkt b/collects/racket/private/kw.rkt index 088e89b490..e0976864cb 100644 --- a/collects/racket/private/kw.rkt +++ b/collects/racket/private/kw.rkt @@ -357,185 +357,228 @@ #f "bad argument sequence" stx (syntax args))])))) ;; The new `lambda' form: + (define-for-syntax (parse-lambda stx non-kw-k kw-k) + (syntax-case stx () + [(_ args body1 body ...) + (if (simple-args? #'args) + ;; Use plain old `lambda': + (non-kw-k + (syntax-protect + (syntax/loc stx + (lambda args body1 body ...)))) + ;; Handle keyword or optional arguments: + (with-syntax ([((plain-id ...) + (opt-id ...) + ([id opt-expr kind] ...) + ([kw kw-id kw-req] ...) + need-kw + rest) + (parse-formals stx #'args)]) + (let ([dup-id (check-duplicate-identifier (syntax->list #'(id ... . rest)))]) + (when dup-id + (raise-syntax-error + #f + "duplicate argument identifier" + stx + dup-id))) + (let* ([kws (syntax->list #'(kw ...))] + [opts (syntax->list #'(opt-id ...))] + [ids (syntax->list #'(id ...))] + [plain-ids (syntax->list #'(plain-id ...))] + [kw-reqs (syntax->list #'(kw-req ...))] + [kw-args (generate-temporaries kws)] ; to hold supplied value + [kw-arg?s (generate-temporaries kws)] ; to indicated whether it was supplied + [opt-args (generate-temporaries opts)] ; supplied value + [opt-arg?s (generate-temporaries opts)] ; whether supplied + [needed-kws (sort (syntax->list #'need-kw) + (lambda (a b) (keywordlist #'(id ... . rest)))]) - (when dup-id - (raise-syntax-error - #f - "duplicate argument identifier" - stx - dup-id))) - (let* ([kws (syntax->list #'(kw ...))] - [opts (syntax->list #'(opt-id ...))] - [ids (syntax->list #'(id ...))] - [plain-ids (syntax->list #'(plain-id ...))] - [kw-reqs (syntax->list #'(kw-req ...))] - [kw-args (generate-temporaries kws)] ; to hold supplied value - [kw-arg?s (generate-temporaries kws)] ; to indicated whether it was supplied - [opt-args (generate-temporaries opts)] ; supplied value - [opt-arg?s (generate-temporaries opts)] ; whether supplied - [needed-kws (sort (syntax->list #'need-kw) - (lambda (a b) (keywordlist stx)]) (if (not (and l (pair? (cdr l)) @@ -762,8 +830,13 @@ #f "missing procedure expression; probably originally (), which is an illegal empty application" stx) - (quasisyntax/loc stx - (#%app . #,(cdr (syntax-e stx)))))) + (begin + (check-arity (- (length l) 2)) + (let ([args (cdr (syntax-e stx))]) + (syntax-protect + (or (generate-direct (cdr (if (pair? args) args (syntax-e args))) null) + (quasisyntax/loc stx + (#%app . #,args)))))))) ;; keyword app (maybe) (let ([exprs (let ([kw-ht (make-hasheq)]) @@ -812,17 +885,20 @@ (keywordsyntax stx '#%app)) + (parse-app (datum->syntax #f (cons #'new-app stx) stx) + (lambda (n) + (when (or (n . < . n-req) + (and (not rest?) + (n . > . (+ n-req n-opt)))) + (printf "~s\n" (list n n-req n-opt)) + (warning "wrong number of by-position arguments"))) + (lambda (args kw-args) + (let* ([args (syntax->list (datum->syntax #f args))] + [n (length args)]) + (and (not (or (n . < . n-req) + (and (not rest?) + (n . > . (+ n-req n-opt))))) + (let loop ([kw-args kw-args] [req-kws req-kws] [all-kws all-kws]) + (cond + [(null? kw-args) + (or (null? req-kws) + (and + (warning + (format "missing required keyword ~a" (car req-kws))) + #f))] + [else (let* ([kw (syntax-e (caar kw-args))] + [all-kws (let loop ([all-kws all-kws]) + (cond + [(null? all-kws) null] + [(keywordsyntax stx (cons wrap-id #'(arg ...)) stx stx)))] + [_ wrap-id]))) ;; Checks given kws against expected. Result is ;; (values missing-kw extra-kw), where both are #f if