0.8alpha.0.8:
[sbcl.git] / src / code / early-type.lisp
index 6dfad22..77698dd 100644 (file)
 ;;; the original type spec.
 (defstruct (hairy-type (:include ctype
                                 (class-info (type-class-or-lose 'hairy))
-                                (enumerable t))
+                                (enumerable t)
+                                (might-contain-other-types-p t))
                       (:copier nil)
                       #!+cmu (:pure nil))
-  ;; the Common Lisp type-specifier
+  ;; the Common Lisp type-specifier of the type we represent
   (specifier nil :type t))
 
 (!define-type-class hairy)
 (defstruct (unknown-type (:include hairy-type)
                         (:copier nil)))
 
+(defstruct (negation-type (:include ctype
+                                   (class-info (type-class-or-lose 'negation))
+                                   ;; FIXME: is this right?  It's
+                                   ;; what they had before, anyway
+                                   (enumerable t)
+                                   (might-contain-other-types-p t))
+                         (:copier nil)
+                         #!+cmu (:pure nil))
+  (type (missing-arg) :type ctype))
+
+(!define-type-class negation)
+
 ;;; ARGS-TYPE objects are used both to represent VALUES types and
 ;;; to represent FUNCTION types.
 (defstruct (args-type (:include ctype)
   ;; true if other &KEY arguments are allowed
   (allowp nil :type boolean))
 
+(defun canonicalize-args-type-args (required optional rest)
+  (when rest
+    (let ((last-distinct-optional (position rest optional
+                                           :from-end t
+                                           :test-not #'type=)))
+      (setf optional
+           (when last-distinct-optional
+             (subseq optional 0 (1+ last-distinct-optional))))))
+  (values required optional rest))
+
+(defun args-types (lambda-list-like-thing)
+  (multiple-value-bind
+       (required optional restp rest keyp keys allowp auxp aux)
+      (parse-lambda-list-like-thing lambda-list-like-thing)
+    (declare (ignore aux))
+    (when auxp
+      (error "&AUX in a FUNCTION or VALUES type: ~S." lambda-list-like-thing))
+    (let ((required (mapcar #'single-value-specifier-type required))
+         (optional (mapcar #'single-value-specifier-type optional))
+         (rest (when restp (single-value-specifier-type rest)))
+         (keywords
+          (collect ((key-info))
+            (dolist (key keys)
+              (unless (proper-list-of-length-p key 2)
+                (error "Keyword type description is not a two-list: ~S." key))
+              (let ((kwd (first key)))
+                (when (find kwd (key-info) :key #'key-info-name)
+                  (error "~@<repeated keyword ~S in lambda list: ~2I~_~S~:>"
+                         kwd lambda-list-like-thing))
+                (key-info
+                 (make-key-info
+                  :name kwd
+                  :type (single-value-specifier-type (second key))))))
+            (key-info))))
+      (multiple-value-bind (required optional rest)
+         (canonicalize-args-type-args required optional rest)
+       (values required optional rest keyp keywords allowp)))))
+                   
 (defstruct (values-type
            (:include args-type
                      (class-info (type-class-or-lose 'values)))
+            (:constructor %make-values-type)
            (:copier nil)))
 
+(defun make-values-type (&rest initargs
+                        &key (args nil argsp) &allow-other-keys)
+  (if argsp
+      (if (eq args '*)
+         *wild-type*
+         (multiple-value-bind (required optional rest keyp keywords allowp)
+             (args-types args)
+           (if (and (null required)
+                    (null optional)
+                    (eq rest *universal-type*)
+                    (not keyp))
+               *wild-type*
+               (%make-values-type :required required
+                                  :optional optional
+                                  :rest rest
+                                  :keyp keyp
+                                  :keywords keywords
+                                  :allowp allowp))))
+      (apply #'%make-values-type initargs)))
+
 (!define-type-class values)
 
 ;;; (SPECIFIER-TYPE 'FUNCTION) and its subtypes
 (defstruct (fun-type (:include args-type
-                              (class-info (type-class-or-lose 'function))))
+                              (class-info (type-class-or-lose 'function)))
+                    (:constructor %make-fun-type))
   ;; true if the arguments are unrestrictive, i.e. *
   (wild-args nil :type boolean)
   ;; type describing the return values. This is a values type
   ;; when multiple values were specified for the return.
   (returns (missing-arg) :type ctype))
+(defun make-fun-type (&rest initargs
+                     &key (args nil argsp) returns &allow-other-keys)
+  (if argsp
+      (if (eq args '*)
+         (if (eq returns *wild-type*)
+             (specifier-type 'function)
+             (%make-fun-type :wild-args t :returns returns))
+         (multiple-value-bind (required optional rest keyp keywords allowp)
+             (args-types args)
+           (if (and (null required)
+                    (null optional)
+                    (eq rest *universal-type*)
+                    (not keyp))
+               (if (eq returns *wild-type*)
+                   (specifier-type 'function)
+                   (%make-fun-type :wild-args t :returns returns))
+               (%make-fun-type :required required
+                               :optional optional
+                               :rest rest
+                               :keyp keyp
+                               :keywords keywords
+                               :allowp allowp
+                               :returns returns))))
+      ;; FIXME: are we really sure that we won't make something that
+      ;; looks like a completely wild function here?
+      (apply #'%make-fun-type initargs)))
 
 ;;; The CONSTANT-TYPE structure represents a use of the CONSTANT-ARG
 ;;; "type specifier", which is only meaningful in function argument
                     (if (consp high)
                         (1- (type-bound-number high))
                         high)))
-           #!+negative-zero-is-not-zero
-           (float
-            ;; Canonicalize a low bound of (-0.0) to 0.0, and a high
-            ;; bound of (+0.0) to -0.0.
-            (values (if (and (consp low)
-                             (floatp (car low))
-                             (zerop (car low))
-                             (minusp (float-sign (car low))))
-                        (float 0.0 (car low))
-                        low)
-                    (if (and (consp high)
-                             (floatp (car high))
-                             (zerop (car high))
-                             (plusp (float-sign (car high))))
-                        (float -0.0 (car high))
-                        high)))
            (t 
             ;; no canonicalization necessary
             (values low high)))
+       (when (and (eq class 'rational)
+                  (integerp canonical-low)
+                  (integerp canonical-high)
+                  (= canonical-low canonical-high))
+         (setf class 'integer))
        (%make-numeric-type :class class
                            :format format
                            :complexp complexp
 ;;; things such as SIMPLE-STRING.
 (defstruct (array-type (:include ctype
                                 (class-info (type-class-or-lose 'array)))
+                       (:constructor %make-array-type)
                       (:copier nil))
   ;; the dimensions of the array, or * if unspecified. If a dimension
   ;; is unspecified, it is *.
   (element-type (missing-arg) :type ctype)
   ;; the element type as it is specialized in this implementation
   (specialized-element-type *wild-type* :type ctype))
+(define-cached-synonym make-array-type)
 
 ;;; A MEMBER-TYPE represent a use of the MEMBER type specifier. We
 ;;; bother with this at this level because MEMBER types are fairly
                                  (class-info (type-class-or-lose 'member))
                                  (enumerable t))
                        (:copier nil)
+                       (:constructor %make-member-type (members))
                        #-sb-xc-host (:pure nil))
   ;; the things in the set, with no duplications
   (members nil :type list))
+(defun make-member-type (&key members)
+  (declare (type list members))
+  ;; make sure that we've removed duplicates
+  (aver (= (length members) (length (remove-duplicates members))))
+  ;; if we have a pair of zeros (e.g. 0.0d0 and -0.0d0), then we can
+  ;; canonicalize to (DOUBLE-FLOAT 0.0d0 0.0d0), because numeric
+  ;; ranges are compared by arithmetic operators (while MEMBERship is
+  ;; compared by EQL).  -- CSR, 2003-04-23
+  (let ((singlep (subsetp '(-0.0f0 0.0f0) members))
+       (doublep (subsetp '(-0.0d0 0.0d0) members))
+       #!+long-float
+       (longp (subsetp '(-0.0l0 0.0l0) members)))
+    (if (or singlep doublep #!+long-float longp)
+       (let (union-types)
+         (when singlep
+           (push (ctype-of 0.0f0) union-types)
+           (setf members (set-difference members '(-0.0f0 0.0f0))))
+         (when doublep
+           (push (ctype-of 0.0d0) union-types)
+           (setf members (set-difference members '(-0.0d0 0.0d0))))
+         #!+long-float
+         (when longp
+           (push (ctype-of 0.0l0) union-types)
+           (setf members (set-difference members '(-0.0l0 0.0l0))))
+         (aver (not (null union-types)))
+         (make-union-type t
+                          (if (null members)
+                              union-types
+                              (cons (%make-member-type members)
+                                    union-types))))
+       (%make-member-type members))))
 
 ;;; A COMPOUND-TYPE is a type defined out of a set of types, the
 ;;; common parent of UNION-TYPE and INTERSECTION-TYPE.
-(defstruct (compound-type (:include ctype)
+(defstruct (compound-type (:include ctype
+                                   (might-contain-other-types-p t))
                          (:constructor nil)
                          (:copier nil))
   (types nil :type list :read-only t))
                                 (class-info (type-class-or-lose 'union)))
                       (:constructor %make-union-type (enumerable types))
                       (:copier nil)))
+(define-cached-synonym make-union-type)
 
 ;;; An INTERSECTION-TYPE represents a use of the AND type specifier
 ;;; which we couldn't canonicalize to something simpler. Canonical form:
       type))
 
 ;;; A CONS-TYPE is used to represent a CONS type.
-(defstruct (cons-type (:include ctype (:class-info (type-class-or-lose 'cons)))
+(defstruct (cons-type (:include ctype (class-info (type-class-or-lose 'cons)))
                      (:constructor
                       ;; ANSI says that for CAR and CDR subtype
                       ;; specifiers '* is equivalent to T. In order
                       ;; possibly elsewhere, we slam all CONS-TYPE
                       ;; objects into canonical form w.r.t. this
                       ;; equivalence at creation time.
-                      make-cons-type (car-raw-type
-                                      cdr-raw-type
-                                      &aux
-                                      (car-type (type-*-to-t car-raw-type))
-                                      (cdr-type (type-*-to-t cdr-raw-type))))
+                      %make-cons-type (car-raw-type
+                                       cdr-raw-type
+                                       &aux
+                                       (car-type (type-*-to-t car-raw-type))
+                                       (cdr-type (type-*-to-t cdr-raw-type))))
                      (:copier nil))
   ;; the CAR and CDR element types (to support ANSI (CONS FOO BAR) types)
   ;;
   ;; FIXME: Most or all other type structure slots could also be :READ-ONLY.
   (car-type (missing-arg) :type ctype :read-only t)
   (cdr-type (missing-arg) :type ctype :read-only t))
+(defun make-cons-type (car-type cdr-type)
+  (if (or (eq car-type *empty-type*)
+         (eq cdr-type *empty-type*))
+      *empty-type*
+      (%make-cons-type car-type cdr-type)))
 \f
 ;;;; type utilities
 
 ;;; type is defined (or redefined).
 (defun-cached (values-specifier-type
               :hash-function (lambda (x)
-                               ;; FIXME: The THE FIXNUM stuff is
-                               ;; redundant in SBCL (or modern CMU
-                               ;; CL) because of type inference.
-                               (the fixnum
-                                    (logand (the fixnum (sxhash x))
-                                            #x3FF)))
+                                (logand (sxhash x) #x3FF))
               :hash-bits 10
               :init-wrapper !cold-init-forms)
-             ((orig eq))
+             ((orig equal-but-no-car-recursion))
   (let ((u (uncross orig)))
     (or (info :type :builtin u)
        (let ((spec (type-expand u)))
           ((and (not (eq spec u))
                 (info :type :builtin spec)))
           ((eq (info :type :kind spec) :instance)
-           (sb!xc:find-class spec))
-          ((typep spec 'class)
+           (find-classoid spec))
+          ((typep spec 'classoid)
            ;; There doesn't seem to be any way to translate
            ;; (TYPEP SPEC 'BUILT-IN-CLASS) into something which can be
            ;; executed on the host Common Lisp at cross-compilation time.
            #+sb-xc-host (error
                          "stub: (TYPEP SPEC 'BUILT-IN-CLASS) on xc host")
-           (if (typep spec 'built-in-class)
-               (or (built-in-class-translation spec) spec)
+           (if (typep spec 'built-in-classoid)
+               (or (built-in-classoid-translation spec) spec)
                spec))
           (t
            (let* (;; FIXME: This automatic promotion of FOO-style
                     (funcall fun lspec))
                    ((or (and (consp spec) (symbolp (car spec)))
                         (symbolp spec))
-                    (when *type-system-initialized*
+                    (when (and *type-system-initialized*
+                                (not (eq (info :type :kind spec)
+                                         :forthcoming-defclass-type)))
                       (signal 'parse-unknown-type :specifier spec))
                     ;; (The RETURN-FROM here inhibits caching.)
                     (return-from values-specifier-type
                     (error "bad thing to be a type specifier: ~S"
                            spec))))))))))
 
-;;; Like VALUES-SPECIFIER-TYPE, except that we guarantee to never
-;;; return a VALUES type.
+;;; This is like VALUES-SPECIFIER-TYPE, except that we guarantee to
+;;; never return a VALUES type.
 (defun specifier-type (x)
   (let ((res (values-specifier-type x)))
     (when (values-type-p res)
       (error "VALUES type illegal in this context:~%  ~S" x))
     res))
 
+(defun single-value-specifier-type (x)
+  (let ((res (specifier-type x)))
+    (if (eq res *wild-type*)
+        *universal-type*
+        res)))
+
 ;;; Similar to MACROEXPAND, but expands DEFTYPEs. We don't bother
 ;;; returning a second value.
 (defun type-expand (form)