(multiple-value-bind (required optional restp rest keyp keys allowp aux)
(parse-lambda-list lambda-list)
(when aux
- (error "&Aux in a FUNCTION or VALUES type: ~S." lambda-list))
+ (error "&AUX in a FUNCTION or VALUES type: ~S." lambda-list))
(setf (args-type-required result) (mapcar #'specifier-type required))
(setf (args-type-optional result) (mapcar #'specifier-type optional))
(setf (args-type-rest result) (if restp (specifier-type rest) nil))
(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: ~S." kwd lambda-list))
+ (error "~@<repeated keyword ~S in lambda list: ~2I~_~S~:>"
+ kwd lambda-list))
(key-info (make-key-info :name kwd
:type (specifier-type (second key))))))
(setf (args-type-keywords result) (key-info)))
;;;; We provide a few special operations that can be meaningfully used
;;;; on VALUES types (as well as on any other type).
-;;; Return the type of the first value indicated by Type. This is used
-;;; by people who don't want to have to deal with values types.
-
-;;; MNA: fix-instance-typep-call patch
+;;; Return the type of the first value indicated by TYPE. This is used
+;;; by people who don't want to have to deal with VALUES types.
#!-sb-fluid (declaim (freeze-type values-type))
; (inline single-value-type))
(defun single-value-type (type)
(cond ((values-type-p type)
(or (car (args-type-required type))
(if (args-type-optional type)
- (type-union (car (args-type-optional type)) (specifier-type 'null)))
+ (type-union (car (args-type-optional type))
+ (specifier-type 'null)))
(args-type-rest type)
(specifier-type 'null)))
((eq type *wild-type*)
(values fixed (+ fixed (length (args-type-optional type))))))
(values nil nil)))
-;;; Determine if Type corresponds to a definite number of values. The
-;;; first value is a list of the types for each value, and the second
-;;; value is the number of values. If the number of values is not
-;;; fixed, then return NIL and :Unknown.
+;;; Determine whether TYPE corresponds to a definite number of values.
+;;; The first value is a list of the types for each value, and the
+;;; second value is the number of values. If the number of values is
+;;; not fixed, then return NIL and :UNKNOWN.
(defun values-types (type)
(declare (type ctype type))
(cond ((eq type *wild-type*)
(values (mapcar #'single-value-type req) (length req))))))
;;; Return two values:
-;;; MNA: fix-instance-typep-call patch
;;; 1. A list of all the positional (fixed and optional) types.
;;; 2. The &REST type (if any). If keywords allowed, *UNIVERSAL-TYPE*.
;;; If no keywords or &REST, then the DEFAULT-TYPE.
(cond ((args-type-keyp type) *universal-type*)
((args-type-rest type))
(t
- ;; MNA: fix-instance-typep-call patch
- default-type))))
+ default-type))))
;;; Return a list of OPERATION applied to the types in TYPES1 and
;;; TYPES2, padding with REST2 as needed. TYPES1 must not be shorter
;;; OPERATION returned true as its second value each time we called
;;; it. Since we approximate the intersection of VALUES types, the
;;; second value being true doesn't mean the result is exact.
-;;; MNA: fix-instance-typep-call patch
(defun args-type-op (type1 type2 operation nreq default-type)
- ;;; MNA: fix-instance-typep-call patch
(declare (type ctype type1 type2 default-type)
(type function operation nreq))
(if (or (values-type-p type1) (values-type-p type2))
(let ((type1 (coerce-to-values type1))
(type2 (coerce-to-values type2)))
(multiple-value-bind (types1 rest1)
- ;;; MNA: fix-instance-typep-call patch
(values-type-types type1 default-type)
(multiple-value-bind (types2 rest2)
- ;;; MNA: fix-instance-typep-call patch
(values-type-types type2 default-type)
(multiple-value-bind (rest rest-exact)
(funcall operation rest1 rest2)
:optional (if opt-last
(subseq opt 0 (1+ opt-last))
())
- ;; MNA fix-instance-typep-call patch
:rest (if (eq rest default-type) nil rest))
(and rest-exact res-exact)))))))))
(funcall operation type1 type2)))
((eq type1 *empty-type*) type2)
((eq type2 *empty-type*) type1)
(t
- ;;; MNA: fix-instance-typep-call patch
(values (args-type-op type1 type2 #'type-union #'min *empty-type*)))))
-;;;
(defun-cached (values-type-intersection :hash-function type-cache-hash
:hash-bits 8
:values 2
(cond ((eq type1 *wild-type*) (values type2 t))
((eq type2 *wild-type*) (values type1 t))
(t
- (args-type-op type1 type2 #'type-intersection #'max (specifier-type 'null)))))
+ (args-type-op type1 type2
+ #'type-intersection
+ #'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
(let ((dims1 (array-type-dimensions type1))
(dims2 (array-type-dimensions type2))
(complexp2 (array-type-complexp type2)))
- ;; See whether dimensions are compatible.
- (cond ((not (or (eq dims2 '*)
+ (cond (;; not subtypep unless dimensions are compatible
+ (not (or (eq dims2 '*)
(and (not (eq dims1 '*))
;; (sbcl-0.6.4 has trouble figuring out that
;; DIMS1 and DIMS2 must be lists at this
(the list dims1)
(the list dims2)))))
(values nil t))
- ;; See whether complexpness is compatible.
+ ;; not subtypep unless complexness is compatible
((not (or (eq complexp2 :maybe)
(eq (array-type-complexp type1) complexp2)))
(values nil t))
- ;; If the TYPE2 eltype is wild, we win. Otherwise, the types
- ;; must be identical.
- ((or (eq (array-type-element-type type2) *wild-type*)
- (type= (specialized-element-type-maybe type1)
- (specialized-element-type-maybe type2)))
+ ;; Since we didn't fail any of the tests above, we win
+ ;; if the TYPE2 element type is wild.
+ ((eq (array-type-element-type type2) *wild-type*)
(values t t))
- (t
- (values nil t)))))
+ (;; Since we didn't match any of the special cases above, we
+ ;; can't give a good answer unless both the element types
+ ;; have been defined.
+ (or (unknown-type-p (array-type-element-type type1))
+ (unknown-type-p (array-type-element-type type2)))
+ (values nil nil))
+ (;; Otherwise, the subtype relationship holds iff the
+ ;; types are equal, and they're equal iff the specialized
+ ;; element types are identical.
+ t
+ (values (type= (specialized-element-type-maybe type1)
+ (specialized-element-type-maybe type2))
+ t)))))
(!define-superclasses array
((string string)
t))
(!define-type-method (member :complex-subtypep-arg1) (type1 type2)
- (block PUNT
- (values (every-type-op ctypep type2 (member-type-members type1)
- :list-first t)
- t)))
+ (values (every-type-op ctypep
+ type2
+ (member-type-members type1)
+ :list-first t)
+ t))
;;; We punt if the odd type is enumerable and intersects with the
;;; MEMBER type. If not enumerable, then it is definitely not a
t)))
(!define-type-method (member :complex-intersection) (type1 type2)
- (block PUNT
- (collect ((members))
- (let ((mem2 (member-type-members type2)))
- (dolist (member mem2)
- (multiple-value-bind (val win) (ctypep member type1)
- (unless win
- (return-from PUNT (values type2 nil)))
- (when val (members member))))
-
- (values (cond ((subsetp mem2 (members)) type2)
- ((null (members)) *empty-type*)
- (t
- (make-member-type :members (members))))
- t)))))
-
-;;; We don't need a :COMPLEX-UNION, since the only interesting case is a union
-;;; type, and the member/union interaction is handled by the union type
-;;; method.
+ (collect ((members))
+ (let ((mem2 (member-type-members type2)))
+ (dolist (member mem2)
+ (multiple-value-bind (val win) (ctypep member type1)
+ (unless win
+ (return-from punt-type-method (values type2 nil)))
+ (when val (members member))))
+
+ (values (cond ((subsetp mem2 (members)) type2)
+ ((null (members)) *empty-type*)
+ (t
+ (make-member-type :members (members))))
+ t))))
+
+;;; We don't need a :COMPLEX-UNION, since the only interesting case is
+;;; a union type, and the member/union interaction is handled by the
+;;; union type method.
(!define-type-method (member :simple-union) (type1 type2)
(let ((mem1 (member-type-members type1))
(mem2 (member-type-members type2)))
(!define-type-method (member :simple-=) (type1 type2)
(let ((mem1 (member-type-members type1))
(mem2 (member-type-members type2)))
- (values (and (subsetp mem1 mem2) (subsetp mem2 mem1))
+ (values (and (subsetp mem1 mem2)
+ (subsetp mem2 mem1))
t)))
(!define-type-method (member :complex-=) (type1 type2)
(make-member-type :members (remove-duplicates members))
*empty-type*))
\f
+;;;; intersection types
+;;;;
+;;;; Until version 0.6.10.6, SBCL followed the original CMU CL approach
+;;;; of punting on all AND types, not just the unreasonably complicated
+;;;; ones. The change was motivated by trying to get the KEYWORD type
+;;;; to behave sensibly:
+;;;; ;; reasonable definition
+;;;; (DEFTYPE KEYWORD () '(AND SYMBOL (SATISFIES KEYWORDP)))
+;;;; ;; reasonable behavior
+;;;; (ASSERT (SUBTYPEP 'KEYWORD 'SYMBOL))
+;;;; Without understanding a little about the semantics of AND, we'd
+;;;; get (SUBTYPEP 'KEYWORD 'SYMBOL)=>NIL,NIL and, for entirely
+;;;; parallel reasons, (SUBTYPEP 'RATIO 'NUMBER)=>NIL,NIL. That's
+;;;; not so good..)
+;;;;
+;;;; We still follow the example of CMU CL to some extent, by punting
+;;;; (to the opaque HAIRY-TYPE) on sufficiently complicated types
+;;;; involving AND.
+
+;;; In general, make an INTERSECTION-TYPE object from the specifier
+;;; types. But in various special cases, dodge instead, representing
+;;; the intersection type in some other way.
+(defun make-intersection-type-or-something (types)
+ (declare (list types))
+ (/show0 "entering MAKE-INTERSECTION-TYPE-OR-SOMETHING")
+ (cond ((null types)
+ *universal-type*)
+ ((null (cdr types))
+ (first types))
+ (;; if potentially too hairy
+ (some (lambda (type)
+ (or (union-type-p type)
+ (hairy-type-p type)))
+ types)
+ ;; (CMU CL punted to HAIRY-TYPE like this for all AND-based
+ ;; types. We don't want to do that for simple intersection
+ ;; types like the definition of KEYWORD, hence the guard
+ ;; clause above. But we do want to punt for any really
+ ;; unreasonable cases which might have motivated them to punt
+ ;; in all cases, hence the punt-to-HAIRY-TYPE code below.)
+ (make-hairy-type :specifier `(and ,@(mapcar #'type-specifier types))))
+ (t
+ (%make-intersection-type (some #'type-enumerable types) types))))
+
+(!define-type-class intersection)
+
+;;; A few intersection types have special names. The others just get
+;;; mechanically unparsed.
+(!define-type-method (intersection :unparse) (type)
+ (declare (type ctype type))
+ (/show0 "entering INTERSECTION :UNPARSE")
+ (or (find type '(ratio bignum keyword) :key #'specifier-type :test #'type=)
+ `(and ,@(mapcar #'type-specifier (intersection-type-types type)))))
+
+;;; shared machinery for type equality: true if every type in the set
+;;; TYPES1 matches a type in the set TYPES2 and vice versa
+(defun type=-set (types1 types2)
+ (/show0 "entering TYPE=-SET")
+ (flet (;; true if every type in the set X matches a type in the set Y
+ (type<=-set (x y)
+ (declare (type list x y))
+ (every (lambda (xelement)
+ (position xelement y :test #'type=))
+ x)))
+ (values (and (type<=-set types1 types2)
+ (type<=-set types2 types1))
+ t)))
+
+;;; Two intersection types are equal if their subtypes are equal sets.
+;;;
+;;; FIXME: Might it be better to use
+;;; (AND (SUBTYPEP X Y) (SUBTYPEP Y X))
+;;; instead, since SUBTYPEP is the usual relationship that we care
+;;; most about, so it would be good to leverage any ingenuity there
+;;; in this more obscure method?
+(!define-type-method (intersection :simple-=) (type1 type2)
+ (/show0 "entering INTERSECTION :SIMPLE-=")
+ (type=-set (intersection-type-types type1)
+ (intersection-type-types type2)))
+
+(!define-type-method (intersection :simple-subtypep) (type1 type2)
+ (declare (type list type1 type2))
+ (/show0 "entering INTERSECTION :SIMPLE-SUBTYPEP")
+ (some (lambda (t1)
+ (every (lambda (t2)
+ (csubtypep t1 t2))
+ type2))
+ type1))
+
+(!define-type-method (intersection :complex-subtypep-arg1) (type1 type2)
+ (/show0 "entering INTERSECTION :COMPLEX-SUBTYPEP-ARG1")
+ (values (any-type-op csubtypep
+ type2
+ (intersection-type-types type1)
+ :list-first t)
+ t))
+
+(!define-type-method (intersection :complex-subtypep-arg2) (type1 type2)
+ (/show0 "entering INTERSECTION :COMPLEX-SUBTYPEP-ARG2")
+ (values (every-type-op csubtypep type1 (intersection-type-types type2))
+ t))
+
+;;; Return a new type list where pairs of types whose intersections
+;;; can be represented simply have been replaced by the simple
+;;; representation.
+(defun simplify-intersection-type-types (%types)
+ (/show0 "entering SIMPLE-INTERSECTION-TYPE-TYPES")
+ (do* ((types (copy-list %types)) ; (to undestructivize the algorithm below)
+ (i-types types (cdr i-types))
+ (i-type (car i-types) (car i-types)))
+ ((null i-types))
+ (do* ((pre-j-types i-types (cdr pre-j-types))
+ (j-types (cdr pre-j-types) (cdr pre-j-types))
+ (j-type (car j-types) (car j-types)))
+ ((null j-types))
+ (multiple-value-bind (isect win) (type-intersection i-type j-type)
+ (when win
+ ;; Overwrite I-TYPES with the intersection, and delete
+ ;; J-TYPES from the list.
+ (setf (car i-types) isect
+ (cdr pre-j-types) (cdr j-types)))))
+ (/show0 "leaving SIMPLE-INTERSECTION-TYPE-TYPES")
+ types))
+
+(!define-type-method (intersection :simple-intersection :complex-intersection)
+ (type1 type2)
+ (/show0 "entering INTERSECTION :SIMPLE-INTERSECTION :COMPLEX-INTERSECTION")
+ (let ((type1types (intersection-type-types type1))
+ (type2types (if (intersection-type-p type2)
+ (intersection-type-types type2)
+ (list type2))))
+ (make-intersection-type-or-something
+ (simplify-intersection-type-types
+ (append type1types type2types)))))
+
+#|
+(!def-type-translator and (&rest type-specifiers)
+ ;; Note: Between the behavior of SIMPLIFY-INTERSECTION-TYPE (which
+ ;; will reduce to a 1-element list any list of types which CMU CL
+ ;; could've represented) and MAKE-INTERSECTION-TYPE-OR-SOMETHING
+ ;; (which knows to treat a 1-element intersection as the element
+ ;; itself) we should recover CMU CL's behavior for anything which it
+ ;; could handle usefully (i.e. could without punting to HAIRY-TYPE).
+ (/show0 "entering type translator for AND")
+ (make-intersection-type-or-something
+ (simplify-intersection-type-types
+ (mapcar #'specifier-type type-specifiers))))
+|#
+;;; (REMOVEME once INTERSECTION-TYPE works.)
+(!def-type-translator and (&whole spec &rest types)
+ (let ((res *wild-type*))
+ (dolist (type types res)
+ (let ((ctype (specifier-type type)))
+ (multiple-value-bind (int win) (type-intersection res ctype)
+ (unless win
+ (return (make-hairy-type :specifier spec)))
+ (setq res int))))))
+\f
;;;; union types
;;; Make a union type from the specifier types, setting ENUMERABLE in
(!define-type-class union)
-;;; If LIST, then return that, otherwise the OR of the component types.
+;;; The LIST type has a special name. Other union types
+;;; just get mechanically unparsed.
(!define-type-method (union :unparse) (type)
(declare (type ctype type))
(if (type= type (specifier-type 'list))
'list
`(or ,@(mapcar #'type-specifier (union-type-types type)))))
-;;; Two union types are equal if every type in one is equal to some
-;;; type in the other.
+;;; Two union types are equal if their subtypes are equal sets.
(!define-type-method (union :simple-=) (type1 type2)
- (block PUNT
- (let ((types1 (union-type-types type1))
- (types2 (union-type-types type2)))
- (values (and (dolist (type1 types1 t)
- (unless (any-type-op type= type1 types2)
- (return nil)))
- (dolist (type2 types2 t)
- (unless (any-type-op type= type2 types1)
- (return nil))))
- t))))
+ (type=-set (union-type-types type1)
+ (union-type-types type2)))
;;; Similarly, a union type is a subtype of another if every element
;;; of TYPE1 is a subtype of some element of TYPE2.
+;;;
+;;; KLUDGE: This definition seems redundant, here in UNION-TYPE and
+;;; similarly in INTERSECTION-TYPE, with the logic in the
+;;; corresponding :COMPLEX-SUBTYPEP-ARG1 and :COMPLEX-SUBTYPEP-ARG2
+;;; methods. Ideally there's probably some way to make the
+;;; :SIMPLE-SUBTYPEP method default to the :COMPLEX-SUBTYPEP-FOO
+;;; methods in such a way that this definition could go away, but I
+;;; don't grok the system well enough to tell whether it's simple to
+;;; arrange this. -- WHN 2000-02-03
(!define-type-method (union :simple-subtypep) (type1 type2)
- (block PUNT
- (let ((types2 (union-type-types type2)))
- (values (dolist (type1 (union-type-types type1) t)
- (unless (any-type-op csubtypep type1 types2)
- (return nil)))
- t))))
+ (let ((types2 (union-type-types type2)))
+ (values (dolist (type1 (union-type-types type1) t)
+ (unless (any-type-op csubtypep type1 types2)
+ (return nil)))
+ t)))
(!define-type-method (union :complex-subtypep-arg1) (type1 type2)
- (block PUNT
- (values (every-type-op csubtypep type2 (union-type-types type1)
- :list-first t)
- t)))
+ (values (every-type-op csubtypep
+ type2
+ (union-type-types type1)
+ :list-first t)
+ t))
(!define-type-method (union :complex-subtypep-arg2) (type1 type2)
- (block PUNT
- (values (any-type-op csubtypep type1 (union-type-types type2)) t)))
+ (values (any-type-op csubtypep type1 (union-type-types type2))
+ t))
(!define-type-method (union :complex-union) (type1 type2)
(let* ((class1 (type-class-info type1)))
(setq res (type-union res t2)))))
(!define-type-method (union :simple-intersection :complex-intersection)
- (type1 type2)
+ (type1 type2)
(let ((res *empty-type*)
(win t))
(dolist (type (union-type-types type2) (values res win))
(setq res (type-union res int))
(unless w (setq win nil))))))
-(!def-type-translator or (&rest types)
+(!def-type-translator or (&rest type-specifiers)
(reduce #'type-union
- (mapcar #'specifier-type types)
+ (mapcar #'specifier-type type-specifiers)
:initial-value *empty-type*))
-
-;;; We don't actually have intersection types, since the result of
-;;; reasonable type intersections is always describable as a union of
-;;; simple types. If something is too hairy to fit this mold, then we
-;;; make a hairy type.
-(!def-type-translator and (&whole spec &rest types)
- (let ((res *wild-type*))
- (dolist (type types res)
- (let ((ctype (specifier-type type)))
- (multiple-value-bind (int win) (type-intersection res ctype)
- (unless win
- (return (make-hairy-type :specifier spec)))
- (setq res int))))))
\f
;;;; CONS types