X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fmacros.lisp;h=ebddde6f300624744c84571638fc82b4e9438040;hb=4603ca100a7d181fe4316429365fc725501336dd;hp=b8603779fc217e827168bdc3c5c074dc0d747b84;hpb=4898ef32c639b1c7f4ee13a5ba566ce6debd03e6;p=sbcl.git diff --git a/src/code/macros.lisp b/src/code/macros.lisp index b860377..ebddde6 100644 --- a/src/code/macros.lisp +++ b/src/code/macros.lisp @@ -56,31 +56,43 @@ ;;; ;;; CHECK-TYPE-ERROR isn't defined until a later file because it uses ;;; the macro RESTART-CASE, which isn't defined until a later file. -(defmacro-mundanely check-type (place type &optional type-string) +(defmacro-mundanely check-type (place type &optional type-string + &environment env) #!+sb-doc - "Signal a restartable error of type TYPE-ERROR if the value of PLACE is - not of the specified type. If an error is signalled and the restart is - used to return, this can only return if the STORE-VALUE restart is - invoked. In that case it will store into PLACE and start over." - (let ((place-value (gensym))) - `(do ((,place-value ,place ,place)) - ((typep ,place-value ',type)) - (setf ,place - (check-type-error ',place ,place-value ',type ,type-string))))) + "Signal a restartable error of type TYPE-ERROR if the value of PLACE +is not of the specified type. If an error is signalled and the restart +is used to return, this can only return if the STORE-VALUE restart is +invoked. In that case it will store into PLACE and start over." + ;; KLUDGE: We use a simpler form of expansion if PLACE is just a + ;; variable to work around Python's blind spot in type derivation. + ;; For more complex places getting the type derived should not + ;; matter so much anyhow. + (let ((expanded (sb!xc:macroexpand place env))) + (if (symbolp expanded) + `(do () + ((typep ,place ',type)) + (setf ,place (check-type-error ',place ,place ',type ,type-string))) + (let ((value (gensym))) + `(do ((,value ,place ,place)) + ((typep ,value ',type)) + (setf ,place + (check-type-error ',place ,value ',type ,type-string))))))) ;;;; DEFINE-SYMBOL-MACRO (defmacro-mundanely define-symbol-macro (name expansion) `(eval-when (:compile-toplevel :load-toplevel :execute) - (sb!c::%define-symbol-macro ',name ',expansion))) + (sb!c::%define-symbol-macro ',name ',expansion (sb!c:source-location)))) -(defun sb!c::%define-symbol-macro (name expansion) +(defun sb!c::%define-symbol-macro (name expansion source-location) (unless (symbolp name) (error 'simple-type-error :datum name :expected-type 'symbol :format-control "Symbol macro name is not a symbol: ~S." :format-arguments (list name))) (with-single-package-locked-error (:symbol name "defining ~A as a symbol-macro")) + (sb!c:with-source-location (source-location) + (setf (info :source-location :symbol-macro name) source-location)) (ecase (info :variable :kind name) ((:macro :global nil) (setf (info :variable :kind name) :macro) @@ -101,22 +113,6 @@ #!+sb-doc "Define a compiler-macro for NAME." (legal-fun-name-or-type-error name) - (when (consp name) - ;; It's fairly clear that the user intends the compiler macro to - ;; expand when he does (SETF (FOO ...) X). And that's even a - ;; useful and reasonable thing to want. Unfortunately, - ;; (SETF (FOO ...) X) macroexpands into (FUNCALL (SETF FOO) X ...), - ;; and it's not at all clear that it's valid to expand a FUNCALL form, - ;; and the ANSI standard doesn't seem to say anything else which - ;; would justify us expanding the compiler macro the way the user - ;; wants. So instead we rely on 3.2.2.1.3 "When Compiler Macros Are - ;; Used" which says they never have to be used, so by ignoring such - ;; macros we're erring on the safe side. But any user who does - ;; (DEFINE-COMPILER-MACRO (SETF FOO) ...) could easily be surprised - ;; by this way of complying with a rather screwy aspect of the ANSI - ;; spec, so at least we can warn him... - (sb!c::compiler-style-warn - "defining compiler macro of (SETF ...), which will not be expanded")) (when (and (symbolp name) (special-operator-p name)) (error 'simple-program-error :format-control "cannot define a compiler-macro for a special operator: ~S" @@ -174,6 +170,23 @@ (eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute) +(define-condition duplicate-case-key-warning (style-warning) + ((key :initarg :key + :reader case-warning-key) + (case-kind :initarg :case-kind + :reader case-warning-case-kind) + (occurrences :initarg :occurrences + :type list + :reader duplicate-case-key-warning-occurrences)) + (:report + (lambda (condition stream) + (format stream + "Duplicate key ~S in ~S form, ~ + occurring in~{~#[~; and~]~{ the ~:R clause:~%~< ~S~:>~}~^,~}." + (case-warning-key condition) + (case-warning-case-kind condition) + (duplicate-case-key-warning-occurrences condition))))) + ;;; CASE-BODY returns code for all the standard "case" macros. NAME is ;;; the macro name, and KEYFORM is the thing to case on. MULTI-P ;;; indicates whether a branch may fire off a list of keys; otherwise, @@ -191,54 +204,73 @@ (warn "no clauses in ~S" name)) (let ((keyform-value (gensym)) (clauses ()) - (keys ())) + (keys ()) + (keys-seen (make-hash-table :test #'eql))) (do* ((cases cases (cdr cases)) - (case (car cases) (car cases))) + (case (car cases) (car cases)) + (case-position 1 (1+ case-position))) ((null cases) nil) - (unless (list-of-length-at-least-p case 1) - (error "~S -- bad clause in ~S" case name)) - (destructuring-bind (keyoid &rest forms) case - (cond (;; an OTHERWISE-CLAUSE - ;; - ;; By the way... The old code here tried gave - ;; STYLE-WARNINGs for normal-clauses which looked as - ;; though they might've been intended to be - ;; otherwise-clauses. As Tony Martinez reported on - ;; sbcl-devel 2004-11-09 there are sometimes good - ;; reasons to write clauses like that; and as I noticed - ;; when trying to understand the old code so I could - ;; understand his patch, trying to guess which clauses - ;; don't have good reasons is fundamentally kind of a - ;; mess. SBCL does issue style warnings rather - ;; enthusiastically, and I have often justified that by - ;; arguing that we're doing that to detect issues which - ;; are tedious for programmers to detect for by - ;; proofreading (like small typoes in long symbol - ;; names, or duplicate function definitions in large - ;; files). This doesn't seem to be an issue like that, - ;; and I can't think of a comparably good justification - ;; for giving STYLE-WARNINGs for legal code here, so - ;; now we just hope the programmer knows what he's - ;; doing. -- WHN 2004-11-20 - (and (not errorp) ; possible only in CASE or TYPECASE, - ; not in [EC]CASE or [EC]TYPECASE - (memq keyoid '(t otherwise)) - (null (cdr cases))) - (push `(t nil ,@forms) clauses)) - ((and multi-p (listp keyoid)) - (setf keys (append keyoid keys)) - (push `((or ,@(mapcar (lambda (key) - `(,test ,keyform-value ',key)) - keyoid)) - nil - ,@forms) - clauses)) - (t - (push keyoid keys) - (push `((,test ,keyform-value ',keyoid) - nil - ,@forms) - clauses))))) + (flet ((check-clause (case-keys) + (loop for k in case-keys + for existing = (gethash k keys-seen) + do (when existing + (let ((sb!c::*current-path* + (when (boundp 'sb!c::*source-paths*) + (or (sb!c::get-source-path case) + sb!c::*current-path*)))) + (warn 'duplicate-case-key-warning + :key k + :case-kind name + :occurrences `(,existing (,case-position (,case))))))) + (let ((record (list case-position (list case)))) + (dolist (k case-keys) + (setf (gethash k keys-seen) record))))) + (unless (list-of-length-at-least-p case 1) + (error "~S -- bad clause in ~S" case name)) + (destructuring-bind (keyoid &rest forms) case + (cond (;; an OTHERWISE-CLAUSE + ;; + ;; By the way... The old code here tried gave + ;; STYLE-WARNINGs for normal-clauses which looked as + ;; though they might've been intended to be + ;; otherwise-clauses. As Tony Martinez reported on + ;; sbcl-devel 2004-11-09 there are sometimes good + ;; reasons to write clauses like that; and as I noticed + ;; when trying to understand the old code so I could + ;; understand his patch, trying to guess which clauses + ;; don't have good reasons is fundamentally kind of a + ;; mess. SBCL does issue style warnings rather + ;; enthusiastically, and I have often justified that by + ;; arguing that we're doing that to detect issues which + ;; are tedious for programmers to detect for by + ;; proofreading (like small typoes in long symbol + ;; names, or duplicate function definitions in large + ;; files). This doesn't seem to be an issue like that, + ;; and I can't think of a comparably good justification + ;; for giving STYLE-WARNINGs for legal code here, so + ;; now we just hope the programmer knows what he's + ;; doing. -- WHN 2004-11-20 + (and (not errorp) ; possible only in CASE or TYPECASE, + ; not in [EC]CASE or [EC]TYPECASE + (memq keyoid '(t otherwise)) + (null (cdr cases))) + (push `(t nil ,@forms) clauses)) + ((and multi-p (listp keyoid)) + (setf keys (append keyoid keys)) + (check-clause keyoid) + (push `((or ,@(mapcar (lambda (key) + `(,test ,keyform-value ',key)) + keyoid)) + nil + ,@forms) + clauses)) + (t + (push keyoid keys) + (check-clause (list keyoid)) + (push `((,test ,keyform-value ',keyoid) + nil + ,@forms) + clauses)))))) (case-body-aux name keyform keyform-value clauses keys errorp proceedp `(,(if multi-p 'member 'or) ,@keys))))