;;;
;;; 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."
+ ;; Detect a common user-error.
+ (when (and (consp type) (eq 'quote (car type)))
+ (error 'simple-reference-error
+ :format-control "Quoted type specifier in ~S: ~S"
+ :format-arguments (list 'check-type type)
+ :references (list '(:ansi-cl :macro check-type))))
+ ;; 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)))))))
\f
;;;; 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"))
- (ecase (info :variable :kind name)
- ((:macro :global nil)
- (setf (info :variable :kind name) :macro)
- (setf (info :variable :macro-expansion name) expansion))
- (:special
- (error 'simple-program-error
- :format-control "Symbol macro name already declared special: ~S."
- :format-arguments (list name)))
- (:constant
- (error 'simple-program-error
- :format-control "Symbol macro name already declared constant: ~S."
- :format-arguments (list name))))
+ (sb!c:with-source-location (source-location)
+ (setf (info :source-location :symbol-macro name) source-location))
+ (let ((kind (info :variable :kind name)))
+ (ecase kind
+ ((:macro :unknown)
+ (setf (info :variable :kind name) :macro)
+ (setf (info :variable :macro-expansion name) expansion))
+ ((:special :global)
+ (error 'simple-program-error
+ :format-control "Symbol macro name already declared ~A: ~S."
+ :format-arguments (list kind name)))
+ (:constant
+ (error 'simple-program-error
+ :format-control "Symbol macro name already defined as a constant: ~S."
+ :format-arguments (list name)))))
name)
\f
;;;; DEFINE-COMPILER-MACRO
#!+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"
;; FIXME: warn about incompatible lambda list with
;; respect to parent function?
(setf (sb!xc:compiler-macro-function name) definition)
- ;; FIXME: Add support for (SETF FDOCUMENTATION) when
- ;; object is a list and type is COMPILER-MACRO. (Until
- ;; then, we have to discard any compiler macro
- ;; documentation for (SETF FOO).)
- (unless (listp name)
- (setf (fdocumentation name 'compiler-macro) doc))
+ (setf (fdocumentation name 'compiler-macro) doc)
,(when set-p
`(case (widetag-of definition)
(#.sb!vm:closure-header-widetag
(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,
(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))))
;; optional dispatch mechanism for the M-V-B gets increasingly
;; hairy.
(if (integerp n)
- (let ((dummy-list nil)
- (keeper (gensym "KEEPER-")))
- ;; We build DUMMY-LIST, a list of variables to bind to useless
- ;; values, then we explicitly IGNORE those bindings and return
- ;; KEEPER, the only thing we're really interested in right now.
- (dotimes (i n)
- (push (gensym "IGNORE-") dummy-list))
+ (let ((dummy-list (make-gensym-list n))
+ (keeper (sb!xc:gensym "KEEPER")))
`(multiple-value-bind (,@dummy-list ,keeper) ,form
(declare (ignore ,@dummy-list))
,keeper))