1.1.13: will be tagged as "sbcl-1.1.13"
[sbcl.git] / src / code / coerce.lisp
index c9630f6..8f1507e 100644 (file)
 
 (in-package "SB!IMPL")
 
-(macrolet ((def-frob (name result access src-type &optional typep)
-            `(defun ,name (object ,@(if typep '(type) ()))
-               (do* ((index 0 (1+ index))
-                     (length (length (the ,(ecase src-type
-                                             (:list 'list)
-                                             (:vector 'vector))
-                                          object)))
-                     (result ,result)
-                     (in-object object))
-                    ((= index length) result)
-                 (declare (fixnum length index))
-                 (setf (,access result index)
-                       ,(ecase src-type
-                          (:list '(pop in-object))
-                          (:vector '(aref in-object index))))))))
+(macrolet ((def (name result access src-type &optional typep)
+             `(defun ,name (object ,@(if typep '(type) ()))
+                (declare (type ,(ecase src-type
+                                       (:list 'list)
+                                       (:vector 'vector)
+                                       (:sequence 'sequence)) object))
+                (do* ((index 0 (1+ index))
+                      (length (length object))
+                      (result ,result)
+                      (in-object object))
+                     ((>= index length) result)
+                  (declare (fixnum length index))
+                  (declare (type vector result))
+                  (setf (,access result index)
+                        ,(ecase src-type
+                           (:list '(pop in-object))
+                           (:vector '(aref in-object index))
+                           (:sequence '(elt in-object index))))))))
 
-  (def-frob list-to-simple-string* (make-string length) schar :list)
-
-  (def-frob list-to-bit-vector* (make-array length :element-type '(mod 2))
-    sbit :list)
-
-  (def-frob list-to-vector* (make-sequence-of-type type length)
+  (def list-to-vector* (make-sequence type length)
     aref :list t)
 
-  (def-frob vector-to-vector* (make-sequence-of-type type length)
+  (def vector-to-vector* (make-sequence type length)
     aref :vector t)
 
-  (def-frob vector-to-simple-string* (make-string length) schar :vector)
-
-  (def-frob vector-to-bit-vector* (make-array length :element-type '(mod 2))
-    sbit :vector))
+  (def sequence-to-vector* (make-sequence type length)
+    aref :sequence t))
 
 (defun vector-to-list* (object)
+  (declare (type vector object))
   (let ((result (list nil))
-       (length (length object)))
+        (length (length object)))
     (declare (fixnum length))
     (do ((index 0 (1+ index))
-        (splice result (cdr splice)))
-       ((= index length) (cdr result))
+         (splice result (cdr splice)))
+        ((>= index length) (cdr result))
       (declare (fixnum index))
       (rplacd splice (list (aref object index))))))
 
-(defun string-to-simple-string* (object)
-  (if (simple-string-p object)
-      object
-      (with-array-data ((data object)
-                       (start)
-                       (end (length object)))
-       (declare (simple-string data))
-       (subseq data start end))))
-
-(defun bit-vector-to-simple-bit-vector* (object)
-  (if (simple-bit-vector-p object)
-      object
-      (with-array-data ((data object)
-                       (start)
-                       (end (length object)))
-       (declare (simple-bit-vector data))
-       (subseq data start end))))
-
 (defvar *offending-datum*); FIXME: Remove after debugging COERCE.
 
 ;;; These are used both by the full DEFUN function and by various
@@ -80,7 +59,7 @@
 ;;; argument type is known. It might be better to do this with
 ;;; DEFTRANSFORMs, though.
 (declaim (inline coerce-to-list))
-(declaim (inline coerce-to-simple-string coerce-to-bit-vector coerce-to-vector))
+(declaim (inline coerce-to-vector))
 (defun coerce-to-fun (object)
   ;; (Unlike the other COERCE-TO-FOOs, this one isn't inline, because
   ;; it's so big and because optimizing away the outer ETYPECASE
     (symbol
      ;; ANSI lets us return ordinary errors (non-TYPE-ERRORs) here.
      (cond ((macro-function object)
-           (error "~S names a macro." object))
-          ((special-operator-p object)
-           (error "~S is a special operator." object))
-          (t (fdefinition object))))
+            (error "~S names a macro." object))
+           ((special-operator-p object)
+            (error "~S is a special operator." object))
+           (t (fdefinition object))))
     (list
      (case (first object)
        ((setf)
-       (fdefinition object))
-       ((lambda instance-lambda)
-       ;; FIXME: If we go to a compiler-only implementation, this can
-       ;; become COMPILE instead of EVAL, which seems nicer to me.
-       (eval `(function ,object)))
+        (fdefinition object))
+       ((lambda)
+        ;; FIXME: If we go to a compiler-only implementation, this can
+        ;; become COMPILE instead of EVAL, which seems nicer to me.
+        (eval `(function ,object)))
        (t
-       (error 'simple-type-error
-              :datum object
-              :expected-type '(or symbol
-                                  ;; KLUDGE: ANSI wants us to
-                                  ;; return a TYPE-ERROR here, and
-                                  ;; a TYPE-ERROR is supposed to
-                                  ;; describe the expected type,
-                                  ;; but it's not obvious how to
-                                  ;; describe the coerceable cons
-                                  ;; types, so we punt and just say
-                                  ;; CONS. -- WHN 20000503
-                                  cons)
-              :format-control "~S can't be coerced to a function."
-              :format-arguments (list object)))))))
+        (error 'simple-type-error
+               :datum object
+               :expected-type '(or symbol
+                                   ;; KLUDGE: ANSI wants us to
+                                   ;; return a TYPE-ERROR here, and
+                                   ;; a TYPE-ERROR is supposed to
+                                   ;; describe the expected type,
+                                   ;; but it's not obvious how to
+                                   ;; describe the coerceable cons
+                                   ;; types, so we punt and just say
+                                   ;; CONS. -- WHN 20000503
+                                   cons)
+               :format-control "~S can't be coerced to a function."
+               :format-arguments (list object)))))))
+
 (defun coerce-to-list (object)
   (etypecase object
     (vector (vector-to-list* object))))
-(defun coerce-to-simple-string (object)
-  (etypecase object
-    (list (list-to-simple-string* object))
-    (string (string-to-simple-string* object))
-    (vector (vector-to-simple-string* object))))
-(defun coerce-to-bit-vector (object)
-  (etypecase object
-    (list (list-to-bit-vector* object))
-    (vector (vector-to-bit-vector* object))))
-(defun coerce-to-simple-vector (x)
-  (if (simple-vector-p x)
-      x
-      (replace (make-array (length x)) x)))
+
 (defun coerce-to-vector (object output-type-spec)
   (etypecase object
     (list (list-to-vector* object output-type-spec))
   #!+sb-doc
   "Coerce the Object to an object of type Output-Type-Spec."
   (flet ((coerce-error ()
-          (/show0 "entering COERCE-ERROR")
-          (error 'simple-type-error
-                 :format-control "~S can't be converted to type ~S."
-                 :format-arguments (list object output-type-spec)))
-        (check-result (result)
-          #!+high-security (aver (typep result output-type-spec))
-          result))
+           (/show0 "entering COERCE-ERROR")
+           (error 'simple-type-error
+                  :format-control "~S can't be converted to type ~S."
+                  :format-arguments (list object output-type-spec)
+                  :datum object
+                  :expected-type output-type-spec)))
     (let ((type (specifier-type output-type-spec)))
       (cond
-       ((%typep object output-type-spec)
-        object)
-       ((eq type *empty-type*)
-        (coerce-error))
-       ((csubtypep type (specifier-type 'character))
-        (character object))
-       ((csubtypep type (specifier-type 'function))
-        #!+high-security
-        (when (and (or (symbolp object)
-                       (and (listp object)
-                            (= (length object) 2)
-                            (eq (car object) 'setf)))
-                   (not (fboundp object)))
-          (error 'simple-type-error
-                 :datum object
-                 :expected-type '(satisfies fboundp)
-              :format-control "~S isn't fbound."
-              :format-arguments (list object)))
-        #!+high-security
-        (when (and (symbolp object)
-                   (sb!xc:macro-function object))
-          (error 'simple-type-error
-                 :datum object
-                 :expected-type '(not (satisfies sb!xc:macro-function))
-                 :format-control "~S is a macro."
-                 :format-arguments (list object)))
-        #!+high-security
-        (when (and (symbolp object)
-                   (special-operator-p object))
-          (error 'simple-type-error
-                 :datum object
-                 :expected-type '(not (satisfies special-operator-p))
-                 :format-control "~S is a special operator."
-                 :format-arguments (list object)))
-        (eval `#',object))
-       ((numberp object)
-        (let ((res
-               (cond
-                 ((csubtypep type (specifier-type 'single-float))
-                  (%single-float object))
-                 ((csubtypep type (specifier-type 'double-float))
-                  (%double-float object))
-                 #!+long-float
-                 ((csubtypep type (specifier-type 'long-float))
-                  (%long-float object))
-                 ((csubtypep type (specifier-type 'float))
-                  (%single-float object))
-                 ((csubtypep type (specifier-type '(complex single-float)))
-                  (complex (%single-float (realpart object))
-                           (%single-float (imagpart object))))
-                 ((csubtypep type (specifier-type '(complex double-float)))
-                  (complex (%double-float (realpart object))
-                           (%double-float (imagpart object))))
-                 #!+long-float
-                 ((csubtypep type (specifier-type '(complex long-float)))
-                  (complex (%long-float (realpart object))
-                           (%long-float (imagpart object))))
-                 ((csubtypep type (specifier-type 'complex))
-                  (complex object))
-                 (t
-                  (coerce-error)))))
-          ;; If RES has the wrong type, that means that rule of canonical
-          ;; representation for complex rationals was invoked. According to
-          ;; the Hyperspec, (coerce 7/2 'complex) returns 7/2. Thus, if the
-          ;; object was a rational, there is no error here.
-          (unless (or (typep res output-type-spec) (rationalp object))
-            (coerce-error))
-          res))
-       ((csubtypep type (specifier-type 'list))
-        (if (vectorp object)
-            (vector-to-list* object)
-            (coerce-error)))
-       ((csubtypep type (specifier-type 'string))
-        (check-result
-         (typecase object
-           (list (list-to-simple-string* object))
-           (string (string-to-simple-string* object))
-           (vector (vector-to-simple-string* object))
-           (t
-            (coerce-error)))))
-       ((csubtypep type (specifier-type 'bit-vector))
-        (check-result
-         (typecase object
-           (list (list-to-bit-vector* object))
-           (vector (vector-to-bit-vector* object))
-           (t
-            (coerce-error)))))
-       ((csubtypep type (specifier-type 'vector))
-        (check-result
-         (typecase object
-           (list (list-to-vector* object output-type-spec))
-           (vector (vector-to-vector* object output-type-spec))
-           (t
-            (coerce-error)))))
-       (t
-        (coerce-error))))))
+        ((%typep object output-type-spec)
+         object)
+        ((eq type *empty-type*)
+         (coerce-error))
+        ((type= type (specifier-type 'character))
+         (character object))
+        ((numberp object)
+         (cond
+           ((csubtypep type (specifier-type 'single-float))
+            (let ((res (%single-float object)))
+              (unless (typep res output-type-spec)
+                (coerce-error))
+              res))
+           ((csubtypep type (specifier-type 'double-float))
+            (let ((res (%double-float object)))
+              (unless (typep res output-type-spec)
+                (coerce-error))
+              res))
+           #!+long-float
+           ((csubtypep type (specifier-type 'long-float))
+            (let ((res (%long-float object)))
+              (unless (typep res output-type-spec)
+                (coerce-error))
+              res))
+           ((csubtypep type (specifier-type 'float))
+            (let ((res (%single-float object)))
+              (unless (typep res output-type-spec)
+                (coerce-error))
+              res))
+           (t
+            (let ((res
+                   (cond
+                     ((csubtypep type (specifier-type '(complex single-float)))
+                      (complex (%single-float (realpart object))
+                               (%single-float (imagpart object))))
+                     ((csubtypep type (specifier-type '(complex double-float)))
+                      (complex (%double-float (realpart object))
+                               (%double-float (imagpart object))))
+                     #!+long-float
+                     ((csubtypep type (specifier-type '(complex long-float)))
+                      (complex (%long-float (realpart object))
+                               (%long-float (imagpart object))))
+                     ((csubtypep type (specifier-type '(complex float)))
+                      (complex (%single-float (realpart object))
+                               (%single-float (imagpart object))))
+                     ((and (typep object 'rational)
+                           (csubtypep type (specifier-type '(complex float))))
+                      ;; Perhaps somewhat surprisingly, ANSI specifies
+                      ;; that (COERCE FOO 'FLOAT) is a SINGLE-FLOAT,
+                      ;; not dispatching on
+                      ;; *READ-DEFAULT-FLOAT-FORMAT*.  By analogy, we
+                      ;; do the same for complex numbers. -- CSR,
+                      ;; 2002-08-06
+                      (complex (%single-float object)))
+                     ((csubtypep type (specifier-type 'complex))
+                      (complex object))
+                     (t
+                      (coerce-error)))))
+              ;; If RES has the wrong type, that means that rule of
+              ;; canonical representation for complex rationals was
+              ;; invoked. According to the Hyperspec, (coerce 7/2
+              ;; 'complex) returns 7/2. Thus, if the object was a
+              ;; rational, there is no error here.
+              (unless (or (typep res output-type-spec)
+                          (rationalp object))
+                (coerce-error))
+              res))))
+        ((csubtypep type (specifier-type 'list))
+         (if (vectorp object)
+             (cond
+               ((type= type (specifier-type 'list))
+                (vector-to-list* object))
+               ((type= type (specifier-type 'null))
+                (if (= (length object) 0)
+                    'nil
+                    (sequence-type-length-mismatch-error type
+                                                         (length object))))
+               ((cons-type-p type)
+                (multiple-value-bind (min exactp)
+                    (sb!kernel::cons-type-length-info type)
+                  (let ((length (length object)))
+                    (if exactp
+                        (unless (= length min)
+                          (sequence-type-length-mismatch-error type length))
+                        (unless (>= length min)
+                          (sequence-type-length-mismatch-error type length)))
+                    (vector-to-list* object))))
+               (t (sequence-type-too-hairy (type-specifier type))))
+             (if (sequencep object)
+                 (cond
+                   ((type= type (specifier-type 'list))
+                    (sb!sequence:make-sequence-like
+                     nil (length object) :initial-contents object))
+                   ((type= type (specifier-type 'null))
+                    (if (= (length object) 0)
+                        'nil
+                        (sequence-type-length-mismatch-error type
+                                                             (length object))))
+                   ((cons-type-p type)
+                    (multiple-value-bind (min exactp)
+                        (sb!kernel::cons-type-length-info type)
+                      (let ((length (length object)))
+                        (if exactp
+                            (unless (= length min)
+                              (sequence-type-length-mismatch-error type length))
+                            (unless (>= length min)
+                              (sequence-type-length-mismatch-error type length)))
+                        (sb!sequence:make-sequence-like
+                         nil length :initial-contents object))))
+                   (t (sequence-type-too-hairy (type-specifier type))))
+                 (coerce-error))))
+        ((csubtypep type (specifier-type 'vector))
+         (typecase object
+           ;; FOO-TO-VECTOR* go through MAKE-SEQUENCE, so length
+           ;; errors are caught there. -- CSR, 2002-10-18
+           (list (list-to-vector* object output-type-spec))
+           (vector (vector-to-vector* object output-type-spec))
+           (sequence (sequence-to-vector* object output-type-spec))
+           (t
+            (coerce-error))))
+        ((and (csubtypep type (specifier-type 'sequence))
+              (find-class output-type-spec nil))
+         (let ((class (find-class output-type-spec)))
+           (unless (sb!mop:class-finalized-p class)
+             (sb!mop:finalize-inheritance class))
+           (sb!sequence:make-sequence-like
+            (sb!mop:class-prototype class)
+            (length object) :initial-contents object)))
+        ((csubtypep type (specifier-type 'function))
+         (when (and (legal-fun-name-p object)
+                    (not (fboundp object)))
+           (error 'simple-type-error
+                  :datum object
+                  ;; FIXME: SATISFIES FBOUNDP is a kinda bizarre broken
+                  ;; type specifier, since the set of values it describes
+                  ;; isn't in general constant in time. Maybe we could
+                  ;; find a better way of expressing this error? (Maybe
+                  ;; with the UNDEFINED-FUNCTION condition?)
+                  :expected-type '(satisfies fboundp)
+               :format-control "~S isn't fbound."
+               :format-arguments (list object)))
+         (when (and (symbolp object)
+                    (sb!xc:macro-function object))
+           (error 'simple-type-error
+                  :datum object
+                  :expected-type '(not (satisfies sb!xc:macro-function))
+                  :format-control "~S is a macro."
+                  :format-arguments (list object)))
+         (when (and (symbolp object)
+                    (special-operator-p object))
+           (error 'simple-type-error
+                  :datum object
+                  :expected-type '(not (satisfies special-operator-p))
+                  :format-control "~S is a special operator."
+                  :format-arguments (list object)))
+         (eval `#',object))
+        (t
+         (coerce-error))))))
 
 ;;; new version, which seems as though it should be better, but which
 ;;; does not yet work
   "Coerces the Object to an object of type Output-Type-Spec."
   (flet ((coerce-error ()
            (error 'simple-type-error
-                 :format-control "~S can't be converted to type ~S."
-                 :format-arguments (list object output-type-spec)))
-        (check-result (result)
-          #!+high-security (aver (typep result output-type-spec))
-          result))
+                  :format-control "~S can't be converted to type ~S."
+                  :format-arguments (list object output-type-spec)))
+         (check-result (result)
+           #!+high-security (aver (typep result output-type-spec))
+           result))
     (let ((type (specifier-type output-type-spec)))
       (cond
-       ((%typep object output-type-spec)
-        object)
-       ((eq type *empty-type*)
-        (coerce-error))
-       ((csubtypep type (specifier-type 'character))
-        (character object))
-       ((csubtypep type (specifier-type 'function))
-        (coerce-to-fun object))
-       ((numberp object)
-        (let ((res
-               (cond
-                 ((csubtypep type (specifier-type 'single-float))
-                  (%single-float object))
-                 ((csubtypep type (specifier-type 'double-float))
-                  (%double-float object))
-                 #!+long-float
-                 ((csubtypep type (specifier-type 'long-float))
-                  (%long-float object))
-                 ((csubtypep type (specifier-type 'float))
-                  (%single-float object))
-                 ((csubtypep type (specifier-type '(complex single-float)))
-                  (complex (%single-float (realpart object))
-                           (%single-float (imagpart object))))
-                 ((csubtypep type (specifier-type '(complex double-float)))
-                  (complex (%double-float (realpart object))
-                           (%double-float (imagpart object))))
-                 #!+long-float
-                 ((csubtypep type (specifier-type '(complex long-float)))
-                  (complex (%long-float (realpart object))
-                           (%long-float (imagpart object))))
-                 ((csubtypep type (specifier-type 'complex))
-                  (complex object))
-                 (t
-                  (coerce-error)))))
-          ;; If RES has the wrong type, that means that rule of
-          ;; canonical representation for complex rationals was
-          ;; invoked. According to the ANSI spec, (COERCE 7/2
-          ;; 'COMPLEX) returns 7/2. Thus, if the object was a
-          ;; rational, there is no error here.
-          (unless (or (typep res output-type-spec) (rationalp object))
-            (coerce-error))
-          res))
-       ((csubtypep type (specifier-type 'list))
-        (coerce-to-list object))
-       ((csubtypep type (specifier-type 'string))
-        (check-result (coerce-to-simple-string object)))
-       ((csubtypep type (specifier-type 'bit-vector))
-        (check-result (coerce-to-bit-vector object)))
-       ((csubtypep type (specifier-type 'vector))
-        (check-result (coerce-to-vector object output-type-spec)))
-       (t
-        (coerce-error))))))
+        ((%typep object output-type-spec)
+         object)
+        ((eq type *empty-type*)
+         (coerce-error))
+        ((csubtypep type (specifier-type 'character))
+         (character object))
+        ((csubtypep type (specifier-type 'function))
+         (coerce-to-fun object))
+        ((numberp object)
+         (let ((res
+                (cond
+                  ((csubtypep type (specifier-type 'single-float))
+                   (%single-float object))
+                  ((csubtypep type (specifier-type 'double-float))
+                   (%double-float object))
+                  #!+long-float
+                  ((csubtypep type (specifier-type 'long-float))
+                   (%long-float object))
+                  ((csubtypep type (specifier-type 'float))
+                   (%single-float object))
+                  ((csubtypep type (specifier-type '(complex single-float)))
+                   (complex (%single-float (realpart object))
+                            (%single-float (imagpart object))))
+                  ((csubtypep type (specifier-type '(complex double-float)))
+                   (complex (%double-float (realpart object))
+                            (%double-float (imagpart object))))
+                  #!+long-float
+                  ((csubtypep type (specifier-type '(complex long-float)))
+                   (complex (%long-float (realpart object))
+                            (%long-float (imagpart object))))
+                  ((csubtypep type (specifier-type 'complex))
+                   (complex object))
+                  (t
+                   (coerce-error)))))
+           ;; If RES has the wrong type, that means that rule of
+           ;; canonical representation for complex rationals was
+           ;; invoked. According to the ANSI spec, (COERCE 7/2
+           ;; 'COMPLEX) returns 7/2. Thus, if the object was a
+           ;; rational, there is no error here.
+           (unless (or (typep res output-type-spec) (rationalp object))
+             (coerce-error))
+           res))
+        ((csubtypep type (specifier-type 'list))
+         (coerce-to-list object))
+        ((csubtypep type (specifier-type 'string))
+         (check-result (coerce-to-simple-string object)))
+        ((csubtypep type (specifier-type 'bit-vector))
+         (check-result (coerce-to-bit-vector object)))
+        ((csubtypep type (specifier-type 'vector))
+         (check-result (coerce-to-vector object output-type-spec)))
+        (t
+         (coerce-error))))))