1.1.13: will be tagged as "sbcl-1.1.13"
[sbcl.git] / src / compiler / ctype.lisp
index 8e1940b..62aa72e 100644 (file)
 ;;; complain about absence of manifest winnage.
 (declaim (ftype (function (list fixnum fun-type) (values)) check-key-args))
 (defun check-key-args (args pre-key type)
-  (do ((key (nthcdr pre-key args) (cddr key))
-       (n (1+ pre-key) (+ n 2)))
-      ((null key))
-    (declare (fixnum n))
-    (let ((k (car key)))
-      (cond
-       ((not (check-arg-type k (specifier-type 'symbol) n)))
-       ((not (constant-lvar-p k))
-        (note-unwinnage "The ~:R argument (in keyword position) is not a ~
-                         constant."
-                        n))
-       (t
-        (let* ((name (lvar-value k))
-               (info (find name (fun-type-keywords type)
-                           :key #'key-info-name)))
-          (cond ((not info)
-                 (unless (fun-type-allowp type)
-                   (note-lossage "~S is not a known argument keyword."
-                                 name)))
-                (t
-                 (check-arg-type (second key) (key-info-type info)
-                                 (1+ n)))))))))
+  (let (lossages allow-other-keys)
+    (do ((key (nthcdr pre-key args) (cddr key))
+         (n (1+ pre-key) (+ n 2)))
+        ((null key))
+      (declare (fixnum n))
+      (let ((k (first key))
+            (v (second key)))
+        (cond
+          ((not (check-arg-type k (specifier-type 'symbol) n)))
+          ((not (constant-lvar-p k))
+           (note-unwinnage "~@<The ~:R argument (in keyword position) is not ~
+                            a constant, weakening keyword argument ~
+                            checking.~:@>" n)
+           ;; An unknown key may turn out to be :ALLOW-OTHER-KEYS at runtime,
+           ;; so we cannot signal full warnings for keys that look bad.
+           (unless allow-other-keys
+             (setf allow-other-keys :maybe)))
+          (t
+           (let* ((name (lvar-value k))
+                  (info (find name (fun-type-keywords type)
+                              :key #'key-info-name)))
+             (cond ((eq name :allow-other-keys)
+                    (unless allow-other-keys
+                      (if (constant-lvar-p v)
+                          (setf allow-other-keys (if (lvar-value v)
+                                                     :yes
+                                                     :no))
+                          (setf allow-other-keys :maybe))))
+                   ((not info)
+                    (unless (fun-type-allowp type)
+                      (pushnew name lossages :test #'eq)))
+                   (t
+                    (check-arg-type (second key) (key-info-type info)
+                                    (1+ n)))))))))
+    (when (and lossages (member allow-other-keys '(nil :no)))
+      (setf lossages (nreverse lossages))
+      (if (cdr lossages)
+          (note-lossage "~@<~{~S~^, ~} and ~S are not a known argument keywords.~:@>"
+                        (butlast lossages)
+                        (car (last lossages)))
+          (note-lossage "~S is not a known argument keyword."
+                        (car lossages)))))
   (values))
 
 ;;; Construct a function type from a definition.
       (let ((name (key-info-name key)))
         (do ((arg args (cddr arg)))
             ((null arg))
-          (when (eq (lvar-value (first arg)) name)
-            (funcall fun (second arg) (key-info-type key))))))))
+          (let ((keyname (first arg)))
+            (when (and (constant-lvar-p keyname)
+                       (eq (lvar-value keyname) name))
+              (funcall fun (second arg) (key-info-type key)))))))))
 
 ;;; Assert that CALL is to a function of the specified TYPE. It is
 ;;; assumed that the call is legal and has only constants in the
     (if trusted
         (derive-node-type call returns)
         (let ((lvar (node-lvar call)))
-          ;; If the value is used in a non-tail position, and
-          ;; the lvar is a single-use, assert the type. Multiple use
-          ;; sites need to be elided because the assertion has to apply
-          ;; to all uses. Tail positions are elided because the assertion
-          ;; would lose cause us not the be in a tail-position anymore.
+          ;; If the value is used in a non-tail position, and the lvar
+          ;; is a single-use, assert the type. Multiple use sites need
+          ;; to be elided because the assertion has to apply to all
+          ;; uses. Tail positions are elided because the assertion
+          ;; would cause us not the be in a tail-position anymore. MV
+          ;; calls are elided because not only are the assertions of
+          ;; less use there, but they can cause the MV call conversion
+          ;; to cause astray.
           (when (and lvar
                      (not (return-p (lvar-dest lvar)))
+                     (not (mv-combination-p (lvar-dest lvar)))
                      (lvar-has-single-use-p lvar))
             (when (assert-lvar-type lvar returns policy)
               (reoptimize-lvar lvar)))))
   (declare (type lvar tag))
   (let ((ctype (lvar-type tag)))
     (when (csubtypep ctype (specifier-type '(or number character)))
-      (compiler-style-warn "~@<using ~S of type ~S as a catch tag (which ~
-                            tends to be unportable because THROW and CATCH ~
-                            use EQ comparison)~@:>"
-                           (lvar-source tag)
-                           (type-specifier (lvar-type tag))))))
-
-(defun %compile-time-type-error (values atype dtype)
+      (let ((sources (lvar-all-sources tag)))
+        (if (singleton-p sources)
+            (compiler-style-warn
+             "~@<using ~S of type ~S as a catch tag (which ~
+                 tends to be unportable because THROW and CATCH ~
+                 use EQ comparison)~@:>"
+             (first sources)
+             (type-specifier (lvar-type tag)))
+            (compiler-style-warn
+             "~@<using ~{~S~^~#[~; or ~:;, ~]~} in ~S of type ~S ~
+                 as a catch tag (which tends to be unportable ~
+                 because THROW and CATCH use EQ comparison)~@:>"
+             (rest sources) (first sources)
+             (type-specifier (lvar-type tag))))))))
+
+(defun %compile-time-type-error (values atype dtype context)
   (declare (ignore dtype))
-  (if (and (consp atype)
-           (eq (car atype) 'values))
-      (error 'values-type-error :datum values :expected-type atype)
-      (error 'type-error :datum (car values) :expected-type atype)))
+  (destructuring-bind (form . detail) context
+    (if (and (consp atype) (eq (car atype) 'values))
+        (if (singleton-p detail)
+            (error 'simple-type-error
+                   :datum (car values)
+                   :expected-type atype
+                   :format-control
+                   "~@<Value set ~2I~_[~{~S~^ ~}] ~I~_from ~S in ~2I~_~S ~I~_is ~
+                   not of type ~2I~_~S.~:>"
+                   :format-arguments (list values
+                                           (first detail) form
+                                           atype))
+            (error 'simple-type-error
+                   :datum (car values)
+                   :expected-type atype
+                   :format-control
+                   "~@<Value set ~2I~_[~{~S~^ ~}] ~
+                   ~I~_from ~2I~_~{~S~^~#[~; or ~:;, ~]~} ~
+                   ~I~_of ~2I~_~S ~I~_in ~2I~_~S ~I~_is not of type ~2I~_~S.~:>"
+                   :format-arguments (list values
+                                           (rest detail) (first detail)
+                                           form
+                                           atype)))
+        (if (singleton-p detail)
+            (error 'simple-type-error
+                   :datum (car values)
+                   :expected-type atype
+                   :format-control "~@<Value of ~S in ~2I~_~S ~I~_is ~2I~_~S, ~
+                                ~I~_not a ~2I~_~S.~:@>"
+                   :format-arguments (list (car detail) form
+                                           (car values)
+                                           atype))
+            (error 'simple-type-error
+                   :datum (car values)
+                   :expected-type atype
+                   :format-control "~@<Value from ~2I~_~{~S~^~#[~; or ~:;, ~]~} ~
+                                   ~I~_of ~2I~_~S ~I~_in ~2I~_~S ~I~_is ~2I~_~S, ~
+                                   ~I~_not a ~2I~_~S.~:@>"
+                   :format-arguments (list (rest detail) (first detail) form
+                                           (car values)
+                                           atype))))))
 
 (defoptimizer (%compile-time-type-error ir2-convert)
-    ((objects atype dtype) node block)
+    ((objects atype dtype context) node block)
   (let ((*compiler-error-context* node))
     (setf (node-source-path node)
           (cdr (node-source-path node)))
-    (destructuring-bind (values atype dtype)
+    (destructuring-bind (values atype dtype context)
         (basic-combination-args node)
       (declare (ignore values))
       (let ((atype (lvar-value atype))
-            (dtype (lvar-value dtype)))
-      (unless (eq atype nil)
-        (warn 'type-warning
-              :format-control
-              "~@<Asserted type ~S conflicts with derived type ~S.~@:>"
-              :format-arguments (list atype dtype)))))
+            (dtype (lvar-value dtype))
+            (detail (cdr (lvar-value context))))
+        (unless (eq atype nil)
+          (if (singleton-p detail)
+              (let ((detail (first detail)))
+                (if (constantp detail)
+                    (warn 'type-warning
+                          :format-control
+                          "~@<Constant ~2I~_~S ~Iconflicts with its ~
+                              asserted type ~2I~_~S.~@:>"
+                          :format-arguments (list (eval detail) atype))
+                    (warn 'type-warning
+                          :format-control
+                          "~@<Derived type of ~S is ~2I~_~S, ~
+                              ~I~_conflicting with ~
+                              its asserted type ~2I~_~S.~@:>"
+                          :format-arguments (list detail dtype atype))))
+              (warn 'type-warning
+                    :format-control
+                    "~@<Derived type of ~2I~_~{~S~^~#[~; and ~:;, ~]~} ~
+                     ~I~_in ~2I~_~S ~I~_is ~2I~_~S, ~I~_conflicting with ~
+                     their asserted type ~2I~_~S.~@:>"
+                    :format-arguments (list (rest detail) (first detail) dtype atype))))))
     (ir2-convert-full-call node block)))