;;;
;;; ASSERT-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 assert (test-form &optional places datum &rest arguments)
+(defmacro-mundanely assert (test-form &optional places datum &rest arguments
+ &environment env)
#!+sb-doc
- "Signals an error if the value of test-form is nil. Continuing from this
- error using the CONTINUE restart will allow the user to alter the value of
- some locations known to SETF, starting over with test-form. Returns NIL."
- `(do () (,test-form)
- (assert-error ',test-form ',places ,datum ,@arguments)
- ,@(mapcar (lambda (place)
- `(setf ,place (assert-prompt ',place ,place)))
- places)))
+ "Signals an error if the value of TEST-FORM is NIL. Returns NIL.
+
+ Optional DATUM and ARGUMENTS can be used to change the signaled
+ error condition and are interpreted as in (APPLY #'ERROR DATUM
+ ARGUMENTS).
+
+ Continuing from the signaled error using the CONTINUE restart will
+ allow the user to alter the values of the SETFable locations
+ specified in PLACES and then start over with TEST-FORM.
+
+ If TEST-FORM is of the form
+
+ (FUNCTION ARG*)
+
+ where FUNCTION is a function (but not a special operator like
+ CL:OR, CL:AND, etc.) the results of evaluating the ARGs will be
+ included in the error report if the assertion fails."
+ (collect ((bindings) (infos))
+ (let ((new-test
+ (flet ((process-place (place)
+ (if (sb!xc:constantp place env)
+ place
+ (with-unique-names (temp)
+ (bindings `(,temp ,place))
+ (infos `(list ',place ,temp))
+ temp))))
+ (cond
+ ;; TEST-FORM looks like a function call. We do not
+ ;; attempt this if TEST-FORM is the application of a
+ ;; special operator because of argument evaluation
+ ;; order issues.
+ ((and (typep test-form '(cons symbol list))
+ (eq (info :function :kind (first test-form)) :function))
+ (let ((name (first test-form))
+ (args (mapcar #'process-place (rest test-form))))
+ `(,name ,@args)))
+ ;; For all other cases, just evaluate TEST-FORM and do
+ ;; not report any details if the assertion fails.
+ (t
+ test-form)))))
+ ;; If TEST-FORM, potentially using values from BINDINGS, does not
+ ;; hold, enter a loop which reports the assertion error,
+ ;; potentially changes PLACES, and retries TEST-FORM.
+ `(tagbody
+ :try
+ (let ,(bindings)
+ (when ,new-test
+ (go :done))
+ (assert-error ',test-form (list ,@(infos))
+ ',places ,datum ,@arguments))
+ ,@(mapcar (lambda (place)
+ `(setf ,place (assert-prompt ',place ,place)))
+ places)
+ (go :try)
+ :done))))
(defun assert-prompt (name value)
(cond ((y-or-n-p "The old value of ~S is ~S.~
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)))
+ (let ((expanded (%macroexpand place env)))
(if (symbolp expanded)
`(do ()
((typep ,place ',type))
(: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)
- (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))))
+ (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
;; 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))
,(when set-p
- `(case (widetag-of definition)
- (#.sb!vm:closure-header-widetag
- (setf (%simple-fun-arglist (%closure-fun definition))
- lambda-list
- (%simple-fun-name (%closure-fun definition))
- debug-name))
- (#.sb!vm:simple-fun-header-widetag
- (setf (%simple-fun-arglist definition) lambda-list
- (%simple-fun-name definition) debug-name))))
+ `(setf (%fun-doc definition) doc
+ (%fun-lambda-list definition) lambda-list
+ (%fun-name definition) debug-name))
name))))
(progn
(def (:load-toplevel :execute) #-sb-xc-host t #+sb-xc-host nil)
(eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
-(define-condition duplicate-case-key-warning (style-warning)
+;;; Make this a full warning during SBCL build.
+(define-condition duplicate-case-key-warning (#-sb-xc-host style-warning #+sb-xc-host warning)
((key :initarg :key
:reader case-warning-key)
(case-kind :initarg :case-kind
,@forms)
clauses))
(t
+ (when (and (eq name 'case)
+ (cdr cases)
+ (memq keyoid '(t otherwise)))
+ (error 'simple-reference-error
+ :format-control
+ "~@<~IBad ~S clause:~:@_ ~S~:@_~S allowed as the key ~
+ designator only in the final otherwise-clause, not in a ~
+ normal-clause. Use (~S) instead, or move the clause the ~
+ correct position.~:@>"
+ :format-arguments (list 'case case keyoid keyoid)
+ :references `((:ansi-cl :macro case))))
(push keyoid keys)
(check-clause (list keyoid))
(push `((,test ,keyform-value ',keyoid)
(cond
,@(nreverse clauses)
,@(if errorp
- `((t (error 'case-failure
- :name ',name
- :datum ,keyform-value
- :expected-type ',expected-type
- :possibilities ',keys))))))))
+ `((t (case-failure ',name ,keyform-value ',keys))))))))
) ; EVAL-WHEN
(defmacro-mundanely case (keyform &body cases)
;; (see FILL-POINTER-OUTPUT-STREAM FIXME in stream.lisp),
;; but it still has to be evaluated for side-effects.
(,element-type-var ,element-type))
- (declare (ignore ,element-type-var))
- ,@decls
- (unwind-protect
- (progn ,@forms)
- (close ,var))))
- `(let ((,var (make-string-output-stream :element-type ,element-type)))
- ,@decls
- (unwind-protect
- (progn ,@forms)
- (close ,var))
- (get-output-stream-string ,var)))))
+ (declare (ignore ,element-type-var))
+ ,@decls
+ (unwind-protect
+ (progn ,@forms)
+ (close ,var))))
+ `(let ((,var (make-string-output-stream
+ ;; CHARACTER is the default element-type of
+ ;; string-ouput-stream, save a few bytes when passing it
+ ,@(and (not (equal element-type ''character))
+ `(:element-type ,element-type)))))
+ ,@decls
+ (unwind-protect
+ (progn ,@forms)
+ (close ,var))
+ (get-output-stream-string ,var)))))
\f
;;;; miscellaneous macros
;; 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))