;;;
;;; RATIO and BIGNUM are not recognized as numeric types.
-;;; FIXME: It seems to me that this should be set to NIL by default,
-;;; and perhaps not even optionally set to T.
-(defvar *use-implementation-types* t
+;;; FIXME: This really should go away. Alas, it doesn't seem to be so
+;;; simple to make it go away.. (See bug 123 in BUGS file.)
+(defvar *use-implementation-types* t ; actually initialized in cold init
#!+sb-doc
"*USE-IMPLEMENTATION-TYPES* is a semi-public flag which determines how
restrictive we are in determining type membership. If two types are the
this switch is on. When it is off, we try to be as restrictive as the
language allows, allowing us to detect more errors. Currently, this only
affects array types.")
-
(!cold-init-forms (setq *use-implementation-types* t))
;;; These functions are used as method for types which need a complex
;;; the description of a &KEY argument
(defstruct (key-info #-sb-xc-host (:pure t)
(:copier nil))
- ;; the key (not necessarily a keyword in ANSI)
- (name (required-argument) :type symbol)
+ ;; the key (not necessarily a keyword in ANSI Common Lisp)
+ (name (missing-arg) :type symbol)
;; the type of the argument value
- (type (required-argument) :type ctype))
+ (type (missing-arg) :type ctype))
(!define-type-method (values :simple-subtypep :complex-subtypep-arg1)
(type1 type2)
;;; a flag that we can bind to cause complex function types to be
;;; unparsed as FUNCTION. This is useful when we want a type that we
;;; can pass to TYPEP.
-(defvar *unparse-function-type-simplify*)
-(!cold-init-forms (setq *unparse-function-type-simplify* nil))
+(defvar *unparse-fun-type-simplify*)
+(!cold-init-forms (setq *unparse-fun-type-simplify* nil))
(!define-type-method (function :unparse) (type)
- (if *unparse-function-type-simplify*
+ (if *unparse-fun-type-simplify*
'function
(list 'function
- (if (function-type-wild-args type)
+ (if (fun-type-wild-args type)
'*
(unparse-args-types type))
(type-specifier
- (function-type-returns type)))))
+ (fun-type-returns type)))))
;;; Since all function types are equivalent to FUNCTION, they are all
;;; subtypes of each other.
(!define-type-class constant :inherits values)
(!define-type-method (constant :unparse) (type)
- `(constant-argument ,(type-specifier (constant-type-type type))))
+ `(constant-arg ,(type-specifier (constant-type-type type))))
(!define-type-method (constant :simple-=) (type1 type2)
(type= (constant-type-type type1) (constant-type-type type2)))
-(!def-type-translator constant-argument (type)
+(!def-type-translator constant-arg (type)
(make-constant-type :type (specifier-type type)))
;;; Given a LAMBDA-LIST-like values type specification and an ARGS-TYPE
(result)))
(!def-type-translator function (&optional (args '*) (result '*))
- (let ((res (make-function-type
- :returns (values-specifier-type result))))
+ (let ((res (make-fun-type :returns (values-specifier-type result))))
(if (eq args '*)
- (setf (function-type-wild-args res) t)
+ (setf (fun-type-wild-args res) t)
(parse-args-types args res))
res))
;;; Return the minimum number of arguments that a function can be
;;; called with, and the maximum number or NIL. If not a function
;;; type, return NIL, NIL.
-(defun function-type-nargs (type)
+(defun fun-type-nargs (type)
(declare (type ctype type))
- (if (function-type-p type)
+ (if (fun-type-p type)
(let ((fixed (length (args-type-required type))))
(if (or (args-type-rest type)
(args-type-keyp type)
(defun fixed-values-op (types1 types2 rest2 operation)
(declare (list types1 types2) (type ctype rest2) (type function operation))
(let ((exact t))
- (values (mapcar #'(lambda (t1 t2)
- (multiple-value-bind (res win)
- (funcall operation t1 t2)
- (unless win
- (setq exact nil))
- res))
+ (values (mapcar (lambda (t1 t2)
+ (multiple-value-bind (res win)
+ (funcall operation t1 t2)
+ (unless win
+ (setq exact nil))
+ res))
types1
(append types2
(make-list (- (length types1) (length types2))
;;; than the precise result.
;;;
;;; The return convention seems to be analogous to
-;;; TYPES-INTERSECT. -- WHN 19990910.
+;;; TYPES-EQUAL-OR-INTERSECT. -- WHN 19990910.
(defun-cached (values-type-union :hash-function type-cache-hash
:hash-bits 8
:default nil
#'max
(specifier-type 'null)))))
-;;; This is like TYPES-INTERSECT, except that it sort of works on
-;;; VALUES types. Note that due to the semantics of
+;;; This is like TYPES-EQUAL-OR-INTERSECT, except that it sort of
+;;; works on VALUES types. Note that due to the semantics of
;;; VALUES-TYPE-INTERSECTION, this might return (VALUES T T) when
-;;; there isn't really any intersection (?).
-;;;
-;;; The return convention seems to be analogous to
-;;; TYPES-INTERSECT. -- WHN 19990910.
-(defun values-types-intersect (type1 type2)
+;;; there isn't really any intersection.
+(defun values-types-equal-or-intersect (type1 type2)
(cond ((or (eq type1 *empty-type*) (eq type2 *empty-type*))
- (values 't t))
+ (values t t))
((or (values-type-p type1) (values-type-p type2))
(multiple-value-bind (res win) (values-type-intersection type1 type2)
(values (not (eq res *empty-type*))
win)))
(t
- (types-intersect type1 type2))))
+ (types-equal-or-intersect type1 type2))))
;;; a SUBTYPEP-like operation that can be used on any types, including
;;; VALUES types
(cond ((eq type2 *wild-type*) (values t t))
((eq type1 *wild-type*)
(values (eq type2 *universal-type*) t))
- ((not (values-types-intersect type1 type2))
+ ((not (values-types-equal-or-intersect type1 type2))
(values nil t))
(t
(if (or (values-type-p type1) (values-type-p type2))
((hairy-type-p type1) type2)
(t type1)))
-;;; The first value is true unless the types don't intersect. The
-;;; second value is true if the first value is definitely correct. NIL
-;;; is considered to intersect with any type. If T is a subtype of
-;;; either type, then we also return T, T. This way we recognize
-;;; that hairy types might intersect with T.
+;;; a test useful for checking whether a derived type matches a
+;;; declared type
;;;
-;;; FIXME: It would be more accurate to call this TYPES-MIGHT-INTERSECT,
-;;; and rename VALUES-TYPES-INTERSECT the same way.
-(defun types-intersect (type1 type2)
+;;; The first value is true unless the types don't intersect and
+;;; aren't equal. The second value is true if the first value is
+;;; definitely correct. NIL is considered to intersect with any type.
+;;; If T is a subtype of either type, then we also return T, T. This
+;;; way we recognize that hairy types might intersect with T.
+(defun types-equal-or-intersect (type1 type2)
(declare (type ctype type1 type2))
(if (or (eq type1 *empty-type*) (eq type2 *empty-type*))
(values t t)
(defvar *wild-type*)
(defvar *empty-type*)
(defvar *universal-type*)
-
+(defvar *universal-fun-type*)
(!cold-init-forms
(macrolet ((frob (name var)
`(progn
;; Ts and *UNIVERSAL-TYPE*s.
(frob * *wild-type*)
(frob nil *empty-type*)
- (frob t *universal-type*)))
+ (frob t *universal-type*))
+ (setf *universal-fun-type*
+ (make-fun-type :wild-args t
+ :returns *wild-type*)))
(!define-type-method (named :simple-=) (type1 type2)
;; FIXME: BUG 85: This assertion failed when I added it in
(aver (not (hairy-type-p type2)))
;; Besides the old CMU CL assertion above, we also need to avoid
;; compound types, else we could get into trouble with
- ;; (SUBTYPEP 'T '(OR (SATISFIES FOO) (SATISFIES BAR)))
+ ;; (SUBTYPEP T '(OR (SATISFIES FOO) (SATISFIES BAR)))
;; or
- ;; (SUBTYPEP 'T '(AND (SATISFIES FOO) (SATISFIES BAR))).
+ ;; (SUBTYPEP T '(AND (SATISFIES FOO) (SATISFIES BAR))).
(aver (not (compound-type-p type2)))
;; Then, since TYPE2 is reasonably tractable, we're good to go.
(values (eq type1 *empty-type*) t))
(if (eq typespec '*)
(make-numeric-type :complexp :complex)
(labels ((not-numeric ()
- ;; FIXME: should probably be TYPE-ERROR
(error "The component type for COMPLEX is not numeric: ~S"
typespec))
+ (not-real ()
+ (error "The component type for COMPLEX is not real: ~S"
+ typespec))
(complex1 (component-type)
(unless (numeric-type-p component-type)
- ;; FIXME: As per the FIXME below, ANSI says we're
- ;; supposed to handle any subtype of REAL, not only
- ;; those which can be represented as NUMERIC-TYPE.
(not-numeric))
(when (eq (numeric-type-complexp component-type) :complex)
- (error "The component type for COMPLEX is complex: ~S"
- typespec))
- (modified-numeric-type component-type :complexp :complex)))
- (let ((type (specifier-type typespec)))
- (typecase type
- ;; This is all that CMU CL handled.
- (numeric-type (complex1 type))
- ;; We need to handle UNION-TYPEs in order to deal with
- ;; REAL and FLOAT being represented as UNION-TYPEs of more
- ;; primitive types.
+ (not-real))
+ (modified-numeric-type component-type :complexp :complex))
+ (complex-union (component)
+ (unless (numberp component)
+ (not-numeric))
+ ;; KLUDGE: This TYPECASE more or less does
+ ;; (UPGRADED-COMPLEX-PART-TYPE (TYPE-OF COMPONENT)),
+ ;; (plus a small hack to treat (EQL COMPONENT 0) specially)
+ ;; but uses logic cut and pasted from the DEFUN of
+ ;; UPGRADED-COMPLEX-PART-TYPE. That's fragile, because
+ ;; changing the definition of UPGRADED-COMPLEX-PART-TYPE
+ ;; would tend to break the code here. Unfortunately,
+ ;; though, reusing UPGRADED-COMPLEX-PART-TYPE here
+ ;; would cause another kind of fragility, because
+ ;; ANSI's definition of TYPE-OF is so weak that e.g.
+ ;; (UPGRADED-COMPLEX-PART-TYPE (TYPE-OF 1/2)) could
+ ;; end up being (UPGRADED-COMPLEX-PART-TYPE 'REAL)
+ ;; instead of (UPGRADED-COMPLEX-PART-TYPE 'RATIONAL).
+ ;; So using TYPE-OF would mean that ANSI-conforming
+ ;; maintenance changes in TYPE-OF could break the code here.
+ ;; It's not clear how best to fix this. -- WHN 2002-01-21,
+ ;; trying to summarize CSR's concerns in his patch
+ (typecase component
+ (complex (error "The component type for COMPLEX (EQL X) ~
+ is complex: ~S"
+ component))
+ ((eql 0) (specifier-type nil)) ; as required by ANSI
+ (single-float (specifier-type '(complex single-float)))
+ (double-float (specifier-type '(complex double-float)))
+ #!+long-float
+ (long-float (specifier-type '(complex long-float)))
+ (rational (specifier-type '(complex rational)))
+ (t (specifier-type '(complex real))))))
+ (let ((ctype (specifier-type typespec)))
+ (typecase ctype
+ (numeric-type (complex1 ctype))
(union-type (apply #'type-union
+ ;; FIXME: This code could suffer from
+ ;; (admittedly very obscure) cases of
+ ;; bug 145 e.g. when TYPE is
+ ;; (OR (AND INTEGER (SATISFIES ODDP))
+ ;; (AND FLOAT (SATISFIES FOO))
+ ;; and not even report the problem very well.
(mapcar #'complex1
- (union-type-types type))))
- ;; FIXME: ANSI just says that TYPESPEC is a subtype of type
- ;; REAL, not necessarily a NUMERIC-TYPE. E.g. TYPESPEC could
- ;; legally be (AND REAL (SATISFIES ODDP))! But like the old
- ;; CMU CL code, we're still not nearly that general.
- (t (not-numeric)))))))
+ (union-type-types ctype))))
+ ;; MEMBER-TYPE is almost the same as UNION-TYPE, but
+ ;; there's a gotcha: (COMPLEX (EQL 0)) is, according to
+ ;; ANSI, equal to type NIL, the empty set.
+ (member-type (apply #'type-union
+ (mapcar #'complex-union
+ (member-type-members ctype))))
+ (t
+ (multiple-value-bind (subtypep certainly)
+ (csubtypep ctype (specifier-type 'real))
+ (if (and (not subtypep) certainly)
+ (not-real)
+ ;; ANSI just says that TYPESPEC is any subtype of
+ ;; type REAL, not necessarily a NUMERIC-TYPE. In
+ ;; particular, at this point TYPESPEC could legally be
+ ;; an intersection type like (AND REAL (SATISFIES ODDP)),
+ ;; in which case we fall through the logic above and
+ ;; end up here, stumped.
+ (bug "~@<(known bug #145): The type ~S is too hairy to be
+ used for a COMPLEX component.~:@>"
+ typespec)))))))))
;;; If X is *, return NIL, otherwise return the bound, which must be a
;;; member of TYPE or a one-element list of a member of TYPE.
nil))
;;; Handle the case of type intersection on two numeric types. We use
-;;; TYPES-INTERSECT to throw out the case of types with no
+;;; TYPES-EQUAL-OR-INTERSECT to throw out the case of types with no
;;; intersection. If an attribute in TYPE1 is unspecified, then we use
;;; TYPE2's attribute, which must be at least as restrictive. If the
;;; types intersect, then the only attributes that can be specified
;; See whether dimensions are compatible.
(cond ((not (or (eq dims1 '*) (eq dims2 '*)
(and (= (length dims1) (length dims2))
- (every #'(lambda (x y)
- (or (eq x '*) (eq y '*) (= x y)))
+ (every (lambda (x y)
+ (or (eq x '*) (eq y '*) (= x y)))
dims1 dims2))))
(values nil t))
;; See whether complexpness is compatible.
;;; subtype of the MEMBER type.
(!define-type-method (member :complex-subtypep-arg2) (type1 type2)
(cond ((not (type-enumerable type1)) (values nil t))
- ((types-intersect type1 type2) (values nil nil))
+ ((types-equal-or-intersect type1 type2) (values nil nil))
(t (values nil t))))
(!define-type-method (member :simple-intersection2) (type1 type2)
(type=-set (intersection-type-types type1)
(intersection-type-types type2)))
-(flet ((intersection-complex-subtypep-arg1 (type1 type2)
- (any/type (swapped-args-fun #'csubtypep)
- type2
- (intersection-type-types type1))))
- (!define-type-method (intersection :simple-subtypep) (type1 type2)
- (every/type #'intersection-complex-subtypep-arg1
- type1
- (intersection-type-types type2)))
- (!define-type-method (intersection :complex-subtypep-arg1) (type1 type2)
- (intersection-complex-subtypep-arg1 type1 type2)))
+(defun %intersection-complex-subtypep-arg1 (type1 type2)
+ (any/type (swapped-args-fun #'csubtypep)
+ type2
+ (intersection-type-types type1)))
+
+(!define-type-method (intersection :simple-subtypep) (type1 type2)
+ (every/type #'%intersection-complex-subtypep-arg1
+ type1
+ (intersection-type-types type2)))
+
+(!define-type-method (intersection :complex-subtypep-arg1) (type1 type2)
+ (%intersection-complex-subtypep-arg1 type1 type2))
(!define-type-method (intersection :complex-subtypep-arg2) (type1 type2)
(every/type #'csubtypep type1 (intersection-type-types type2)))
(multiple-value-bind (val win) (csubtypep x-type y-type)
(unless win (return-from type-difference nil))
(when val (return))
- (when (types-intersect x-type y-type)
+ (when (types-equal-or-intersect x-type y-type)
(return-from type-difference nil))))))
(let ((y-mem (find-if #'member-type-p y-types)))
(when y-mem
\f
;;;; utilities shared between cross-compiler and target system
+;;; Does the type derived from compilation of an actual function
+;;; definition satisfy declarations of a function's type?
+(defun defined-ftype-matches-declared-ftype-p (defined-ftype declared-ftype)
+ (declare (type ctype defined-ftype declared-ftype))
+ (flet ((is-built-in-class-function-p (ctype)
+ (and (built-in-class-p ctype)
+ (eq (built-in-class-%name ctype) 'function))))
+ (cond (;; DECLARED-FTYPE could certainly be #<BUILT-IN-CLASS FUNCTION>;
+ ;; that's what happens when we (DECLAIM (FTYPE FUNCTION FOO)).
+ (is-built-in-class-function-p declared-ftype)
+ ;; In that case, any definition satisfies the declaration.
+ t)
+ (;; It's not clear whether or how DEFINED-FTYPE might be
+ ;; #<BUILT-IN-CLASS FUNCTION>, but it's not obviously
+ ;; invalid, so let's handle that case too, just in case.
+ (is-built-in-class-function-p defined-ftype)
+ ;; No matter what DECLARED-FTYPE might be, we can't prove
+ ;; that an object of type FUNCTION doesn't satisfy it, so
+ ;; we return success no matter what.
+ t)
+ (;; Otherwise both of them must be FUN-TYPE objects.
+ t
+ ;; FIXME: For now we only check compatibility of the return
+ ;; type, not argument types, and we don't even check the
+ ;; return type very precisely (as per bug 94a). It would be
+ ;; good to do a better job. Perhaps to check the
+ ;; compatibility of the arguments, we should (1) redo
+ ;; VALUES-TYPES-EQUAL-OR-INTERSECT as
+ ;; ARGS-TYPES-EQUAL-OR-INTERSECT, and then (2) apply it to
+ ;; the ARGS-TYPE slices of the FUN-TYPEs. (ARGS-TYPE
+ ;; is a base class both of VALUES-TYPE and of FUN-TYPE.)
+ (values-types-equal-or-intersect
+ (fun-type-returns defined-ftype)
+ (fun-type-returns declared-ftype))))))
+
;;; This messy case of CTYPE for NUMBER is shared between the
;;; cross-compiler and the target system.
(defun ctype-of-number (x)
:low low
:high high))))
\f
-(!defun-from-collected-cold-init-forms !late-type-cold-init)
+(locally
+ ;; Why SAFETY 0? To suppress the is-it-the-right-structure-type
+ ;; checking for declarations in structure accessors. Otherwise we
+ ;; can get caught in a chicken-and-egg bootstrapping problem, whose
+ ;; symptom on x86 OpenBSD sbcl-0.pre7.37.flaky5.22 is an illegal
+ ;; instruction trap. I haven't tracked it down, but I'm guessing it
+ ;; has to do with setting LAYOUTs when the LAYOUT hasn't been set
+ ;; yet. -- WHN
+ (declare (optimize (safety 0)))
+ (!defun-from-collected-cold-init-forms !late-type-cold-init))
(/show0 "late-type.lisp end of file")