;;;; This file contains implementation-dependent parts of the type
;;;; support code. This is stuff which deals with the mapping from
;;;; types defined in Common Lisp to types actually supported by an
;;;; implementation.

;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.

(in-package "SB!KERNEL")

;;;; implementation-dependent DEFTYPEs

;;; Make DOUBLE-FLOAT a synonym for LONG-FLOAT, SINGLE-FLOAT for
;;; SHORT-FLOAT. This is expanded before the translator gets a chance,
;;; so we will get precedence.
#!-long-float
(setf (info :type :kind 'long-float) :defined)
#!-long-float
(sb!xc:deftype long-float (&optional low high)
  `(double-float ,low ,high))
(setf (info :type :kind 'short-float) :defined)
(sb!xc:deftype short-float (&optional low high)
  `(single-float ,low ,high))

;;; an index into an integer
(sb!xc:deftype bit-index () `(integer 0 ,sb!xc:most-positive-fixnum))

;;; worst-case values for float attributes
(sb!xc:deftype float-exponent ()
  #!-long-float 'double-float-exponent
  #!+long-float 'long-float-exponent)
(sb!xc:deftype float-digits ()
  #!-long-float `(integer 0 ,sb!vm:double-float-digits)
  #!+long-float `(integer 0 ,sb!vm:long-float-digits))
(sb!xc:deftype float-radix () '(integer 2 2))
(sb!xc:deftype float-int-exponent ()
  #!-long-float 'double-float-int-exponent
  #!+long-float 'long-float-int-exponent)

;;; a code for BOOLE
(sb!xc:deftype boole-code () '(unsigned-byte 4))

;;; a byte specifier (as generated by BYTE)
(sb!xc:deftype byte-specifier () 'cons)

;;; result of CHAR-INT
(sb!xc:deftype char-int () 'char-code)

;;; PATHNAME pieces, as returned by the PATHNAME-xxx functions
(sb!xc:deftype pathname-host () '(or sb!impl::host null))
(sb!xc:deftype pathname-device ()
  '(or simple-string (member nil :unspecific :unc)))
(sb!xc:deftype pathname-directory () 'list)
(sb!xc:deftype pathname-name ()
  '(or simple-string sb!impl::pattern (member nil :unspecific :wild)))
(sb!xc:deftype pathname-type ()
  '(or simple-string sb!impl::pattern (member nil :unspecific :wild)))
(sb!xc:deftype pathname-version ()
  '(or integer (member nil :newest :wild :unspecific)))

;;; internal time format. (Note: not a FIXNUM, ouch..)
(sb!xc:deftype internal-time () 'unsigned-byte)

(sb!xc:deftype bignum-element-type () `(unsigned-byte ,sb!vm:n-word-bits))
(sb!xc:deftype bignum-type () 'bignum)
;;; FIXME: see also DEFCONSTANT MAXIMUM-BIGNUM-LENGTH in
;;; src/code/bignum.lisp.  -- CSR, 2004-07-19
(sb!xc:deftype bignum-index ()
  '(integer 0 #.(1- (ash 1 (- sb!vm:n-word-bits sb!vm:n-widetag-bits)))))

;;;; hooks into the type system

(sb!xc:deftype unboxed-array (&optional dims)
  (collect ((types (list 'or)))
    (dolist (type *specialized-array-element-types*)
      (when (subtypep type '(or integer character float (complex float)))
        (types `(array ,type ,dims))))
    (types)))

(sb!xc:deftype simple-unboxed-array (&optional dims)
  (collect ((types (list 'or)))
    (dolist (type *specialized-array-element-types*)
      (when (subtypep type '(or integer character float (complex float)))
        (types `(simple-array ,type ,dims))))
    (types)))

(sb!xc:deftype complex-vector (&optional element-type length)
  `(and (vector ,element-type ,length) (not simple-array)))

;;; Return the symbol that describes the format of FLOAT.
(declaim (ftype (function (float) symbol) float-format-name))
(defun float-format-name (x)
  (etypecase x
    (single-float 'single-float)
    (double-float 'double-float)
    #!+long-float (long-float 'long-float)))

;;; This function is called when the type code wants to find out how
;;; an array will actually be implemented. We set the
;;; SPECIALIZED-ELEMENT-TYPE to correspond to the actual
;;; specialization used in this implementation.
(declaim (ftype (function (array-type) array-type) specialize-array-type))
(defun specialize-array-type (type)
  (let ((eltype (array-type-element-type type)))
    (setf (array-type-specialized-element-type type)
          (if (or (eq eltype *wild-type*)
                  ;; This is slightly dubious, but not as dubious as
                  ;; assuming that the upgraded-element-type should be
                  ;; equal to T, given the way that the AREF
                  ;; DERIVE-TYPE optimizer works.  -- CSR, 2002-08-19
                  (contains-unknown-type-p eltype))
              *wild-type*
              (dolist (stype-name *specialized-array-element-types*
                                  *universal-type*)
                ;; FIXME: Mightn't it be better to have
                ;; *SPECIALIZED-ARRAY-ELEMENT-TYPES* be stored as precalculated
                ;; SPECIFIER-TYPE results, instead of having to calculate
                ;; them on the fly this way? (Call the new array
                ;; *SPECIALIZED-ARRAY-ELEMENT-SPECIFIER-TYPES* or something..)
                (let ((stype (specifier-type stype-name)))
                  (aver (not (unknown-type-p stype)))
                  (when (csubtypep eltype stype)
                    (return stype))))))
    type))

(defun sb!xc:upgraded-array-element-type (spec &optional environment)
  #!+sb-doc
  "Return the element type that will actually be used to implement an array
   with the specifier :ELEMENT-TYPE Spec."
  (declare (ignore environment))
  (handler-case
      ;; Can't rely on SPECIFIER-TYPE to signal PARSE-UNKNOWN-TYPE in
      ;; the case of (AND KNOWN UNKNOWN), since the result of the
      ;; outter call to SPECIFIER-TYPE can be cached by the code that
      ;; doesn't catch PARSE-UNKNOWN-TYPE signal.
      (if (contains-unknown-type-p (specifier-type spec))
          (error "Undefined type: ~S" spec)
          (type-specifier (array-type-specialized-element-type
                           (specifier-type `(array ,spec)))))
    (parse-unknown-type (c)
      (error "Undefined type: ~S" (parse-unknown-type-specifier c)))))

(defun sb!xc:upgraded-complex-part-type (spec &optional environment)
  #!+sb-doc
  "Return the element type of the most specialized COMPLEX number type that
   can hold parts of type SPEC."
  (declare (ignore environment))
  (let ((type (specifier-type spec)))
    (cond
      ((eq type *empty-type*) nil)
      ((unknown-type-p type) (error "undefined type: ~S" spec))
      (t
       (let ((ctype (specifier-type `(complex ,spec))))
         (cond
           ((eq ctype *empty-type*) '(eql 0))
           ((csubtypep ctype (specifier-type '(complex single-float)))
            'single-float)
           ((csubtypep ctype (specifier-type '(complex double-float)))
            'double-float)
           #!+long-float
           ((csubtypep ctype (specifier-type '(complex long-float)))
            'long-float)
           ((csubtypep ctype (specifier-type '(complex rational)))
            'rational)
           (t 'real)))))))

;;; Return the most specific integer type that can be quickly checked that
;;; includes the given type.
(defun containing-integer-type (subtype)
  (dolist (type `(fixnum
                  (signed-byte ,sb!vm:n-word-bits)
                  (unsigned-byte ,sb!vm:n-word-bits)
                  integer)
                (error "~S isn't an integer type?" subtype))
    (when (csubtypep subtype (specifier-type type))
      (return type))))

;;; If TYPE has a CHECK-xxx template, but doesn't have a corresponding
;;; PRIMITIVE-TYPE, then return the template's name. Otherwise, return NIL.
(defun hairy-type-check-template-name (type)
  (declare (type ctype type))
  (typecase type
    (cons-type
     (if (type= type (specifier-type 'cons))
         'sb!c:check-cons
         nil))
    (built-in-classoid
     (if (type= type (specifier-type 'symbol))
         'sb!c:check-symbol
         nil))
    (numeric-type
     (cond ((type= type (specifier-type 'fixnum))
            'sb!c:check-fixnum)
           #!+#.(cl:if (cl:= 32 sb!vm:n-word-bits) '(and) '(or))
           ((type= type (specifier-type '(signed-byte 32)))
            'sb!c:check-signed-byte-32)
           #!+#.(cl:if (cl:= 32 sb!vm:n-word-bits) '(and) '(or))
           ((type= type (specifier-type '(unsigned-byte 32)))
            'sb!c:check-unsigned-byte-32)
           #!+#.(cl:if (cl:= 64 sb!vm:n-word-bits) '(and) '(or))
           ((type= type (specifier-type '(signed-byte 64)))
            'sb!c:check-signed-byte-64)
           #!+#.(cl:if (cl:= 64 sb!vm:n-word-bits) '(and) '(or))
           ((type= type (specifier-type '(unsigned-byte 64)))
            'sb!c:check-unsigned-byte-64)
           (t nil)))
    (fun-type
     'sb!c:check-fun)
    (t
     nil)))
