;;; constant. At worst, it will convert to %TYPEP, which will prevent
;;; spurious attempts at transformation (and possible repeated
;;; warnings.)
-(deftransform typep ((object type) * * :node node)
+(deftransform typep ((object type &optional env) * * :node node)
(unless (constant-lvar-p type)
(give-up-ir1-transform "can't open-code test of non-constant type"))
+ (unless (and (constant-lvar-p env) (null (lvar-value env)))
+ (give-up-ir1-transform "environment argument present and not null"))
(multiple-value-bind (expansion fail-p)
(source-transform-typep 'object (lvar-value type))
(if fail-p
`(%typep ,object ',spec))
(t
(ecase (first spec)
- (satisfies `(if (funcall #',(second spec) ,object) t nil))
+ (satisfies
+ `(if (funcall (global-function ,(second spec)) ,object) t nil))
((not and)
(once-only ((n-obj object))
`(,(first spec) ,@(mapcar (lambda (x)
;;; Return forms to test that OBJ has the rank and dimensions
;;; specified by TYPE, where STYPE is the type we have checked against
;;; (which is the same but for dimensions and element type).
+;;;
+;;; Secondary return value is true if passing the generated tests implies that
+;;; the array has a header.
(defun test-array-dimensions (obj type stype)
(declare (type array-type type stype))
(let ((obj `(truly-the ,(type-specifier stype) ,obj))
(unless (or (eq dims '*)
(equal dims (array-type-dimensions stype)))
(cond ((cdr dims)
- `((array-header-p ,obj)
- ,@(when (eq (array-type-dimensions stype) '*)
- `((= (%array-rank ,obj) ,(length dims))))
- ,@(loop for d in dims
- for i from 0
- unless (eq '* d)
- collect `(= (%array-dimension ,obj ,i) ,d))))
- ((and dims (csubtypep stype (specifier-type 'simple-array)))
- `((not (array-header-p ,obj))
- ,@(unless (eq '* (car dims))
- `((= (vector-length ,obj) ,@dims)))))
- ((and dims (csubtypep stype (specifier-type '(and array (not simple-array)))))
- `((array-header-p ,obj)
- ,@(unless (eq '* (car dims))
- `((= (%array-dimension ,obj 0) ,@dims)))))
- (dims
- (unless (eq '* (car dims))
- `((if (array-header-p ,obj)
- (= (%array-dimension ,obj 0) ,@dims)
- (= (vector-length ,obj) ,@dims)))))))))
+ (values `((array-header-p ,obj)
+ ,@(when (eq (array-type-dimensions stype) '*)
+ `((= (%array-rank ,obj) ,(length dims))))
+ ,@(loop for d in dims
+ for i from 0
+ unless (eq '* d)
+ collect `(= (%array-dimension ,obj ,i) ,d)))
+ t))
+ ((not dims)
+ (values `((array-header-p ,obj)
+ (= (%array-rank ,obj) 0))
+ t))
+ ((not (array-type-complexp type))
+ (if (csubtypep stype (specifier-type 'vector))
+ (values (unless (eq '* (car dims))
+ `((= (vector-length ,obj) ,@dims)))
+ nil)
+ (values (if (eq '* (car dims))
+ `((not (array-header-p ,obj)))
+ `((not (array-header-p ,obj))
+ (= (vector-length ,obj) ,@dims)))
+ nil)))
+ (t
+ (values (unless (eq '* (car dims))
+ `((if (array-header-p ,obj)
+ (= (%array-dimension ,obj 0) ,@dims)
+ (= (vector-length ,obj) ,@dims))))
+ nil))))))
-;;; Return forms to test that OBJ has the element-type specified by
-;;; type specified by TYPE, where STYPE is the type we have checked
-;;; against (which is the same but for dimensions and element type).
-(defun test-array-element-type (obj type stype)
+;;; Return forms to test that OBJ has the element-type specified by type
+;;; specified by TYPE, where STYPE is the type we have checked against (which
+;;; is the same but for dimensions and element type). If HEADERP is true, OBJ
+;;; is guaranteed to be an array-header.
+(defun test-array-element-type (obj type stype headerp)
(declare (type array-type type stype))
(let ((obj `(truly-the ,(type-specifier stype) ,obj))
(eltype (array-type-specialized-element-type type)))
- (unless (type= eltype (array-type-specialized-element-type stype))
- (with-unique-names (data)
- `((do ((,data ,obj (%array-data-vector ,data)))
- ((not (array-header-p ,data))
- ;; KLUDGE: this isn't in fact maximally efficient,
- ;; because though we know that DATA is a (SIMPLE-ARRAY *
- ;; (*)), we will still check to see if the lowtag is
- ;; appropriate.
- (typep ,data
- '(simple-array ,(type-specifier eltype) (*))))))))))
+ (unless (or (type= eltype (array-type-specialized-element-type stype))
+ (eq eltype *wild-type*))
+ (let ((typecode (sb!vm:saetp-typecode (find-saetp-by-ctype eltype))))
+ (with-unique-names (data)
+ (if (and headerp (not (array-type-complexp stype)))
+ ;; If we know OBJ is an array header, and that the array is
+ ;; simple, we also know there is exactly one indirection to
+ ;; follow.
+ `((eq (%other-pointer-widetag (%array-data-vector ,obj)) ,typecode))
+ `((do ((,data ,(if headerp `(%array-data-vector ,obj) obj)
+ (%array-data-vector ,data)))
+ ((not (array-header-p ,data))
+ (eq (%other-pointer-widetag ,data) ,typecode))))))))))
;;; If we can find a type predicate that tests for the type without
;;; dimensions, then use that predicate and test for dimensions.
;; not safe to assume here that it will eventually
;; have (UPGRADED-ARRAY-ELEMENT-TYPE type)=T, so punt.)
(not (unknown-type-p (array-type-element-type type)))
- (eq (array-type-complexp stype) (array-type-complexp type)))
+ (or (eq (array-type-complexp stype) (array-type-complexp type))
+ (and (eql (array-type-complexp stype) :maybe)
+ (eql (array-type-complexp type) t))))
(once-only ((n-obj obj))
- `(and (,pred ,n-obj)
- ,@(test-array-dimensions n-obj type stype)
- ,@(test-array-element-type n-obj type stype)))
+ (multiple-value-bind (tests headerp)
+ (test-array-dimensions n-obj type stype)
+ `(and (,pred ,n-obj)
+ ,@(when (and (eql (array-type-complexp stype) :maybe)
+ (eql (array-type-complexp type) t))
+ ;; KLUDGE: this is a bit lame; if we get here,
+ ;; we already know that N-OBJ is an array, but
+ ;; (NOT SIMPLE-ARRAY) doesn't know that. On the
+ ;; other hand, this should get compiled down to
+ ;; two widetag tests, so it's only a bit lame.
+ `((typep ,n-obj '(not simple-array))))
+ ,@tests
+ ,@(test-array-element-type n-obj type stype headerp))))
`(%typep ,obj ',(type-specifier type)))))
;;; Transform a type test against some instance type. The type test is
(t nil))
`(%typep ,object ',type))))
-(define-source-transform typep (object spec)
+(define-source-transform typep (object spec &optional env)
;; KLUDGE: It looks bad to only do this on explicitly quoted forms,
;; since that would overlook other kinds of constants. But it turns
;; out that the DEFTRANSFORM for TYPEP detects any constant
;; lvar, transforms it into a quoted form, and gives this
;; source transform another chance, so it all works out OK, in a
;; weird roundabout way. -- WHN 2001-03-18
- (if (and (consp spec)
+ (if (and (not env)
+ (consp spec)
(eq (car spec) 'quote)
(or (not *allow-instrumenting*)
(policy *lexenv* (= store-coverage-data 0))))
(deftransform coerce ((x type) (* *) * :node node)
(unless (constant-lvar-p type)
(give-up-ir1-transform))
- (let ((tspec (ir1-transform-specifier-type (lvar-value type))))
+ (let* ((tval (lvar-value type))
+ (tspec (ir1-transform-specifier-type tval)))
(if (csubtypep (lvar-type x) tspec)
'x
;; Note: The THE here makes sure that specifiers like
;; FIXME: #!+long-float (t ,(error "LONG-FLOAT case needed"))
((csubtypep tspec (specifier-type 'float))
'(%single-float x))
- ((and (csubtypep tspec (specifier-type 'simple-vector))
- ;; Can we avoid checking for dimension issues like
- ;; (COERCE FOO '(SIMPLE-VECTOR 5)) returning a
- ;; vector of length 6?
- (or (policy node (< safety 3)) ; no need in unsafe code
- (and (array-type-p tspec) ; no need when no dimensions
- (equal (array-type-dimensions tspec) '(*)))))
- `(if (simple-vector-p x)
+ ;; Special case STRING and SIMPLE-STRING as they are union types
+ ;; in SBCL.
+ ((member tval '(string simple-string))
+ `(if (typep x ',tval)
+ x
+ (replace (make-array (length x) :element-type 'character) x)))
+ ;; Special case VECTOR
+ ((eq tval 'vector)
+ `(if (vectorp x)
x
(replace (make-array (length x)) x)))
- ;; FIXME: other VECTOR types?
+ ;; Handle specialized element types for 1D arrays.
+ ((csubtypep tspec (specifier-type '(array * (*))))
+ ;; Can we avoid checking for dimension issues like (COERCE FOO
+ ;; '(SIMPLE-VECTOR 5)) returning a vector of length 6?
+ (if (or (policy node (< safety 3)) ; no need in unsafe code
+ (and (array-type-p tspec) ; no need when no dimensions
+ (equal (array-type-dimensions tspec) '(*))))
+ ;; We can!
+ (let ((array-type
+ (if (csubtypep tspec (specifier-type 'simple-array))
+ 'simple-array
+ 'array)))
+ (dolist (etype
+ #+sb-xc-host '(t bit character)
+ #-sb-xc-host sb!kernel::*specialized-array-element-types*
+ (give-up-ir1-transform))
+ (when etype
+ (let ((spec `(,array-type ,etype (*))))
+ (when (csubtypep tspec (specifier-type spec))
+ ;; Is the result required to be non-simple?
+ (let ((result-simple
+ (or (eq 'simple-array array-type)
+ (neq *empty-type*
+ (type-intersection
+ tspec (specifier-type 'simple-array))))))
+ (return
+ `(if (typep x ',spec)
+ x
+ (replace
+ (make-array (length x) :element-type ',etype
+ ,@(unless result-simple
+ (list :fill-pointer t
+ :adjustable t)))
+ x)))))))))
+ ;; No, duh. Dimension checking required.
+ (give-up-ir1-transform
+ "~@<~S specifies dimensions other than (*) in safe code.~:@>"
+ tval)))
(t
- (give-up-ir1-transform)))))))
-
-
+ (give-up-ir1-transform
+ "~@<open coding coercion to ~S not implemented.~:@>"
+ tval)))))))