Move poorly-named NWORDS function near its call site
[sbcl.git] / src / code / typep.lisp
blob4f1bec233634d3112f529a42e0983adb93127213
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
3 ;;;;
4 ;;;; This software is derived from the CMU CL system, which was
5 ;;;; written at Carnegie Mellon University and released into the
6 ;;;; public domain. The software is in the public domain and is
7 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
8 ;;;; files for more information.
10 (in-package "SB!KERNEL")
12 ;;; (Note that when cross-compiling, SB!XC:TYPEP is interpreted as a
13 ;;; test that the host Lisp object OBJECT translates to a target SBCL
14 ;;; type TYPE. This behavior is needed e.g. to test for the validity
15 ;;; of numeric subtype bounds read when cross-compiling.)
16 (defun typep (object type &optional environment)
17 "Is OBJECT of type TYPE?"
18 (declare (type lexenv-designator environment) (ignore environment))
19 (declare (explicit-check))
20 ;; Actually interpreting types at runtime is done by %TYPEP. The
21 ;; cost of the extra function call here should be negligible
22 ;; compared to the cost of interpreting types. (And the compiler
23 ;; tries hard to optimize away the interpretation of types at
24 ;; runtime, and when it succeeds, we never get here anyway.)
25 (%%typep object (specifier-type type)))
27 ;;; the actual TYPEP engine. The compiler only generates calls to this
28 ;;; function when it can't figure out anything more intelligent to do.
29 (defun %typep (object specifier)
30 ;; Checking CTYPE-P on the specifier, as used to be done, is not right.
31 ;; If the specifier were a CTYPE we shouldn't have gotten here.
32 (declare (explicit-check))
33 (%%typep object (specifier-type specifier)))
35 (defun %%typep (object type &optional (strict t))
36 (declare (type ctype type))
37 (etypecase type
38 (named-type
39 (ecase (named-type-name type)
40 ((* t) t)
41 ((instance) (%instancep object))
42 ((funcallable-instance) (funcallable-instance-p object))
43 ((extended-sequence) (extended-sequence-p object))
44 ((nil) nil)))
45 (numeric-type
46 (and (numberp object)
47 (let (;; I think this works because of an invariant of the
48 ;; two components of a COMPLEX are always coerced to
49 ;; be the same, e.g. (COMPLEX 1.0 3/2) => #C(1.0 1.5).
50 ;; Dunno why that holds, though -- ANSI? Python
51 ;; tradition? marsh faerie spirits? -- WHN 2001-10-27
52 (num (if (complexp object)
53 (realpart object)
54 object)))
55 (ecase (numeric-type-class type)
56 (integer (integerp num))
57 (rational (rationalp num))
58 (float
59 (ecase (numeric-type-format type)
60 (short-float (typep num 'short-float))
61 (single-float (typep num 'single-float))
62 (double-float (typep num 'double-float))
63 (long-float (typep num 'long-float))
64 ((nil) (floatp num))))
65 ((nil) t)))
66 (flet ((bound-test (val)
67 (let ((low (numeric-type-low type))
68 (high (numeric-type-high type)))
69 (and (cond ((null low) t)
70 ((listp low) (> val (car low)))
71 (t (>= val low)))
72 (cond ((null high) t)
73 ((listp high) (< val (car high)))
74 (t (<= val high)))))))
75 (ecase (numeric-type-complexp type)
76 ((nil) t)
77 (:complex
78 (and (complexp object)
79 (bound-test (realpart object))
80 (bound-test (imagpart object))))
81 (:real
82 (and (not (complexp object))
83 (bound-test object)))))))
84 (array-type
85 (and (arrayp object)
86 (or (eq (array-type-complexp type) :maybe)
87 (eq (not (simple-array-p object))
88 (array-type-complexp type)))
89 (let ((want (array-type-dimensions type)))
90 (or (eq want '*)
91 (if (array-header-p object)
92 (do ((rank (array-rank object))
93 (axis 0 (1+ axis))
94 (want want (cdr want)))
95 ((= axis rank) (null want))
96 (let ((dim (car want)))
97 (unless (or (eq dim '*)
98 (eq dim (%array-dimension object axis)))
99 (return nil))))
100 (let ((dim (car want)))
101 (and (or (eq dim '*) (eq dim (length object)))
102 (not (cdr want)))))))
103 ;; FIXME: treatment of compound types involving unknown types
104 ;; is generally bogus throughout the system, e.g.
105 ;; (TYPEP MY-ARRAY '(ARRAY (OR BAD1 BAD2) *)) => T
106 ;; because (OR BAD1 BAD2) is not represented as an UNKNOWN-TYPE,
107 ;; and has specialized type '*.
108 ;; One way to fix this is that every CTYPE needs a bit to indicate
109 ;; whether any subpart of it is unknown, or else when parsing,
110 ;; we should always return an UNKNOWN if any subpart is unknown,
111 ;; or else any time we use a CTYPE, we do a deep traversal
112 ;; to detect embedded UNKNOWNs (which seems bad for performance).
113 (if (unknown-type-p (array-type-element-type type))
114 ;; better to fail this way than to get bogosities like
115 ;; (TYPEP (MAKE-ARRAY 11) '(ARRAY SOME-UNDEFINED-TYPE)) => T
116 (error "~@<unknown element type in array type: ~2I~_~S~:>"
117 (type-specifier type))
119 (or (eq (array-type-specialized-element-type type) *wild-type*)
120 (values (type= (array-type-specialized-element-type type)
121 ;; FIXME: not the most efficient.
122 (specifier-type (array-element-type
123 object)))))))
124 (member-type
125 (when (member-type-member-p object type)
127 (classoid
128 #+sb-xc-host (ctypep object type)
129 ;; It might be more efficient to check that OBJECT is either INSTANCEP
130 ;; or FUNCALLABLE-INSTANCE-P before making this call.
131 ;; But doing that would change the behavior if %%TYPEP were ever called
132 ;; with a built-in classoid whose members are not instances.
133 ;; e.g. (%%typep (find-fdefn 'car) (specifier-type 'fdefn))
134 ;; I'm not sure if that can happen.
135 #-sb-xc-host (classoid-typep (layout-of object) type object))
136 (union-type
137 (some (lambda (union-type-type) (%%typep object union-type-type strict))
138 (union-type-types type)))
139 (intersection-type
140 (every (lambda (intersection-type-type)
141 (%%typep object intersection-type-type strict))
142 (intersection-type-types type)))
143 (cons-type
144 (and (consp object)
145 (%%typep (car object) (cons-type-car-type type) strict)
146 (%%typep (cdr object) (cons-type-cdr-type type) strict)))
147 #!+sb-simd-pack
148 (simd-pack-type
149 (and (simd-pack-p object)
150 (let* ((tag (%simd-pack-tag object))
151 (name (nth tag *simd-pack-element-types*)))
152 (not (not (member name (simd-pack-type-element-type type)))))))
153 (character-set-type
154 (and (characterp object)
155 (let ((code (char-code object))
156 (pairs (character-set-type-pairs type)))
157 (dolist (pair pairs nil)
158 (destructuring-bind (low . high) pair
159 (when (<= low code high)
160 (return t)))))))
161 (unknown-type
162 ;; dunno how to do this ANSIly -- WHN 19990413
163 #+sb-xc-host (error "stub: %%TYPEP UNKNOWN-TYPE in xcompilation host")
164 ;; Parse it again to make sure it's really undefined.
165 (let ((reparse (specifier-type (unknown-type-specifier type))))
166 (if (typep reparse 'unknown-type)
167 (error "unknown type specifier: ~S"
168 (unknown-type-specifier reparse))
169 (%%typep object reparse strict))))
170 (negation-type
171 (not (%%typep object (negation-type-type type) strict)))
172 (hairy-type
173 ;; Now the tricky stuff.
174 (let* ((hairy-spec (hairy-type-specifier type))
175 (symbol (car hairy-spec)))
176 (ecase symbol
177 (and
178 (every (lambda (spec) (%%typep object (specifier-type spec) strict))
179 (rest hairy-spec)))
180 ;; Note: it should be safe to skip OR here, because union
181 ;; types can always be represented as UNION-TYPE in general
182 ;; or other CTYPEs in special cases; we never need to use
183 ;; HAIRY-TYPE for them.
184 (not
185 (unless (proper-list-of-length-p hairy-spec 2)
186 (error "invalid type specifier: ~S" hairy-spec))
187 (not (%%typep object (specifier-type (cadr hairy-spec)) strict)))
188 (satisfies
189 (unless (proper-list-of-length-p hairy-spec 2)
190 (error "invalid type specifier: ~S" hairy-spec))
191 (values (funcall (symbol-function (cadr hairy-spec)) object))))))
192 (alien-type-type
193 (sb!alien-internals:alien-typep object (alien-type-type-alien-type type)))
194 (fun-type
195 (if strict
196 (error "Function types are not a legal argument to TYPEP:~% ~S"
197 (type-specifier type))
198 (and (functionp object)
199 (csubtypep (specifier-type (sb!impl::%fun-type object)) type))))))
201 (defun cached-typep (cache object)
202 (let* ((type (cdr cache))
203 (ctype (if (ctype-p type)
204 type
205 (specifier-type type))))
206 (if (unknown-type-p ctype)
207 (%%typep object ctype)
208 ;; Most of the time an undefined type becomes defined is
209 ;; through structure or class definition, optimize that case
210 (let ((fun
211 (if (classoid-p ctype)
212 (lambda (cache object)
213 ;; TODO: structures can be optimized even further
214 (block nil
215 (classoid-typep
216 (typecase object
217 (instance (%instance-layout object))
218 (funcallable-instance
219 (%funcallable-instance-layout object))
220 (t (return)))
221 (cdr (truly-the cons cache))
222 object)))
223 (lambda (cache object)
224 (%%typep object (cdr (truly-the cons cache)))))))
225 (setf (cdr cache) ctype)
226 (sb!thread:barrier (:write))
227 (setf (car cache) fun)
228 (funcall fun cache object)))))
230 ;;; Do a type test from a class cell, allowing forward reference and
231 ;;; redefinition.
232 (defun classoid-cell-typep (cell object)
233 (let ((layout (typecase object
234 (instance (%instance-layout object))
235 (funcallable-instance (%funcallable-instance-layout object))
236 (t (return-from classoid-cell-typep))))
237 (classoid (classoid-cell-classoid cell)))
238 (unless classoid
239 (error "The class ~S has not yet been defined."
240 (classoid-cell-name cell)))
241 (classoid-typep layout classoid object)))
243 ;;; Test whether OBJ-LAYOUT is from an instance of CLASSOID.
244 (defun classoid-typep (obj-layout classoid object)
245 ;; FIXME & KLUDGE: We could like to grab the *WORLD-LOCK* here (to ensure that
246 ;; class graph doesn't change while we're doing the typep test), but in
247 ;; practice that causes trouble -- deadlocking against the compiler
248 ;; if compiler output (or macro, or compiler-macro expansion) causes
249 ;; another thread to do stuff. Not locking is a shoddy bandaid as it is remains
250 ;; easy to trigger the same problem using a different code path -- but in practice
251 ;; locking here makes Slime unusable with :SPAWN in post *WORLD-LOCK* world. So...
252 ;; -- NS 2008-12-16
253 (multiple-value-bind (obj-layout layout)
254 (do ((layout (classoid-layout classoid) (classoid-layout classoid))
255 (i 0 (+ i 1))
256 (obj-layout obj-layout))
257 ((and (not (layout-invalid obj-layout))
258 (not (layout-invalid layout)))
259 (values obj-layout layout))
260 (aver (< i 2))
261 (when (layout-invalid obj-layout)
262 (setq obj-layout (update-object-layout-or-invalid object layout)))
263 (%ensure-classoid-valid classoid layout "typep"))
264 (let ((obj-inherits (layout-inherits obj-layout)))
265 (or (eq obj-layout layout)
266 (dotimes (i (length obj-inherits) nil)
267 (when (eq (svref obj-inherits i) layout)
268 (return t)))))))