-(declaim (ftype (sfunction (continuation) ctype) continuation-type))
-(defun continuation-type (cont)
- (single-value-type (continuation-derived-type cont)))
-
-;;; If CONT is an argument of a function, return a type which the
-;;; function checks CONT for.
-#!-sb-fluid (declaim (inline continuation-externally-checkable-type))
-(defun continuation-externally-checkable-type (cont)
- (or (continuation-%externally-checkable-type cont)
- (%continuation-%externally-checkable-type cont)))
-(defun %continuation-%externally-checkable-type (cont)
- (declare (type continuation cont))
- (let ((dest (continuation-dest cont)))
- (if (not (and dest (combination-p dest)))
- ;; TODO: MV-COMBINATION
- (setf (continuation-%externally-checkable-type cont) *wild-type*)
- (let* ((fun (combination-fun dest))
- (args (combination-args dest))
- (fun-type (continuation-type fun)))
- (setf (continuation-%externally-checkable-type fun) *wild-type*)
- (if (or (not (fun-type-p fun-type))
- ;; FUN-TYPE might be (AND FUNCTION (SATISFIES ...)).
- (fun-type-wild-args fun-type))
- (progn (dolist (arg args)
- (when arg
- (setf (continuation-%externally-checkable-type arg)
- *wild-type*)))
- *wild-type*)
- (let* ((arg-types (append (fun-type-required fun-type)
- (fun-type-optional fun-type)
- (let ((rest (list (or (fun-type-rest fun-type)
- *wild-type*))))
- (setf (cdr rest) rest)))))
- ;; TODO: &KEY
- (loop
- for arg of-type continuation in args
- and type of-type ctype in arg-types
- do (when arg
- (setf (continuation-%externally-checkable-type arg)
- (coerce-to-values type))))
- (continuation-%externally-checkable-type cont)))))))
-(declaim (inline flush-continuation-externally-checkable-type))
-(defun flush-continuation-externally-checkable-type (cont)
- (declare (type continuation cont))
- (setf (continuation-%externally-checkable-type cont) nil))
+(declaim (ftype (sfunction (lvar) ctype) lvar-type))
+(defun lvar-type (lvar)
+ (single-value-type (lvar-derived-type lvar)))
+
+;;; LVAR-CONSERVATIVE-TYPE
+;;;
+;;; Certain types refer to the contents of an object, which can
+;;; change without type derivation noticing: CONS types and ARRAY
+;;; types suffer from this:
+;;;
+;;; (let ((x (the (cons fixnum fixnum) (cons a b))))
+;;; (setf (car x) c)
+;;; (+ (car x) (cdr x)))
+;;;
+;;; Python doesn't realize that the SETF CAR can change the type of X -- so we
+;;; cannot use LVAR-TYPE which gets the derived results. Worse, still, instead
+;;; of (SETF CAR) we might have a call to a user-defined function FOO which
+;;; does the same -- so there is no way to use the derived information in
+;;; general.
+;;;
+;;; So, the conservative option is to use the derived type if the leaf has
+;;; only a single ref -- in which case there cannot be a prior call that
+;;; mutates it. Otherwise we use the declared type or punt to the most general
+;;; type we know to be correct for sure.
+(defun lvar-conservative-type (lvar)
+ (let ((derived-type (lvar-type lvar))
+ (t-type *universal-type*))
+ ;; Recompute using NODE-CONSERVATIVE-TYPE instead of derived type if
+ ;; necessary -- picking off some easy cases up front.
+ (cond ((or (eq derived-type t-type)
+ ;; Can't use CSUBTYPEP!
+ (type= derived-type (specifier-type 'list))
+ (type= derived-type (specifier-type 'null)))
+ derived-type)
+ ((and (cons-type-p derived-type)
+ (eq t-type (cons-type-car-type derived-type))
+ (eq t-type (cons-type-cdr-type derived-type)))
+ derived-type)
+ ((and (array-type-p derived-type)
+ (or (not (array-type-complexp derived-type))
+ (let ((dimensions (array-type-dimensions derived-type)))
+ (or (eq '* dimensions)
+ (every (lambda (dim) (eq '* dim)) dimensions)))))
+ derived-type)
+ ((type-needs-conservation-p derived-type)
+ (single-value-type (lvar-type-using lvar node-conservative-type)))
+ (t
+ derived-type))))
+
+(defun node-conservative-type (node)
+ (let* ((derived-values-type (node-derived-type node))
+ (derived-type (single-value-type derived-values-type)))
+ (if (ref-p node)
+ (let ((leaf (ref-leaf node)))
+ (if (and (basic-var-p leaf)
+ (cdr (leaf-refs leaf)))
+ (coerce-to-values
+ (if (eq :declared (leaf-where-from leaf))
+ (leaf-type leaf)
+ (conservative-type derived-type)))
+ derived-values-type))
+ derived-values-type)))
+
+(defun conservative-type (type)
+ (cond ((or (eq type *universal-type*)
+ (eq type (specifier-type 'list))
+ (eq type (specifier-type 'null)))
+ type)
+ ((cons-type-p type)
+ (specifier-type 'cons))
+ ((array-type-p type)
+ (if (array-type-complexp type)
+ (make-array-type
+ ;; ADJUST-ARRAY may change dimensions, but rank stays same.
+ :dimensions
+ (let ((old (array-type-dimensions type)))
+ (if (eq '* old)
+ old
+ (mapcar (constantly '*) old)))
+ ;; Complexity cannot change.
+ :complexp (array-type-complexp type)
+ ;; Element type cannot change.
+ :element-type (array-type-element-type type)
+ :specialized-element-type (array-type-specialized-element-type type))
+ ;; Simple arrays cannot change at all.
+ type))
+ (t
+ ;; If the type contains some CONS types, the conservative type contains all
+ ;; of them.
+ (when (types-equal-or-intersect type (specifier-type 'cons))
+ (setf type (type-union type (specifier-type 'cons))))
+ ;; Similarly for non-simple arrays -- it should be possible to preserve
+ ;; more information here, but really...
+ (let ((non-simple-arrays (specifier-type '(and array (not simple-array)))))
+ (when (types-equal-or-intersect type non-simple-arrays)
+ (setf type (type-union type non-simple-arrays))))
+ type)))
+
+(defun type-needs-conservation-p (type)
+ (cond ((eq type *universal-type*)
+ ;; Excluding T is necessary, because we do want type derivation to
+ ;; be able to narrow it down in case someone (most like a macro-expansion...)
+ ;; actually declares something as having type T.
+ nil)
+ ((or (cons-type-p type) (and (array-type-p type) (array-type-complexp type)))
+ ;; Covered by the next case as well, but this is a quick test.
+ t)
+ ((types-equal-or-intersect type (specifier-type '(or cons (and array (not simple-array)))))
+ t)))
+
+;;; If LVAR is an argument of a function, return a type which the
+;;; function checks LVAR for.
+#!-sb-fluid (declaim (inline lvar-externally-checkable-type))
+(defun lvar-externally-checkable-type (lvar)
+ (or (lvar-%externally-checkable-type lvar)
+ (%lvar-%externally-checkable-type lvar)))
+(defun %lvar-%externally-checkable-type (lvar)
+ (declare (type lvar lvar))
+ (let ((dest (lvar-dest lvar)))
+ (if (not (and dest (combination-p dest)))
+ ;; TODO: MV-COMBINATION
+ (setf (lvar-%externally-checkable-type lvar) *wild-type*)
+ (let* ((fun (combination-fun dest))
+ (args (combination-args dest))
+ (fun-type (lvar-type fun)))
+ (setf (lvar-%externally-checkable-type fun) *wild-type*)
+ (if (or (not (call-full-like-p dest))
+ (not (fun-type-p fun-type))
+ ;; FUN-TYPE might be (AND FUNCTION (SATISFIES ...)).
+ (fun-type-wild-args fun-type))
+ (dolist (arg args)
+ (when arg
+ (setf (lvar-%externally-checkable-type arg)
+ *wild-type*)))
+ (map-combination-args-and-types
+ (lambda (arg type)
+ (setf (lvar-%externally-checkable-type arg)
+ (acond ((lvar-%externally-checkable-type arg)
+ (values-type-intersection
+ it (coerce-to-values type)))
+ (t (coerce-to-values type)))))
+ dest)))))
+ (or (lvar-%externally-checkable-type lvar) *wild-type*))
+#!-sb-fluid(declaim (inline flush-lvar-externally-checkable-type))
+(defun flush-lvar-externally-checkable-type (lvar)
+ (declare (type lvar lvar))
+ (setf (lvar-%externally-checkable-type lvar) nil))