Inherit FP modes for new threads on Windows.
[sbcl.git] / src / code / macros.lisp
index 3da5529..3cbc7af 100644 (file)
 ;;;
 ;;; 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))
@@ -93,18 +147,19 @@ invoked. In that case it will store into PLACE and start over."
       (: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
@@ -145,22 +200,10 @@ invoked. In that case it will store into PLACE and start over."
              ;; 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)
@@ -170,7 +213,8 @@ invoked. In that case it will store into PLACE and start over."
 
 (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
@@ -216,7 +260,7 @@ invoked. In that case it will store into PLACE and start over."
                      do (when existing
                           (let ((sb!c::*current-path*
                                  (when (boundp 'sb!c::*source-paths*)
-                                   (or (gethash case sb!c::*source-paths*)
+                                   (or (sb!c::get-source-path case)
                                        sb!c::*current-path*))))
                             (warn 'duplicate-case-key-warning
                                   :key k
@@ -265,6 +309,17 @@ invoked. In that case it will store into PLACE and start over."
                          ,@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)
@@ -308,11 +363,7 @@ invoked. In that case it will store into PLACE and start over."
          (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)
@@ -420,17 +471,21 @@ invoked. In that case it will store into PLACE and start over."
                  ;; (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
 
@@ -446,13 +501,8 @@ invoked. In that case it will store into PLACE and start over."
   ;; 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))