Fix handling of few or no xrefs in REPACK-XREF
[sbcl.git] / src / pcl / slots.lisp
blobfdb314782842a94cf035d37709cc739dc9771781
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
4 ;;;; This software is derived from software originally released by Xerox
5 ;;;; Corporation. Copyright and release statements follow. Later modifications
6 ;;;; to the software are in the public domain and are provided with
7 ;;;; absolutely no warranty. See the COPYING and CREDITS files for more
8 ;;;; information.
10 ;;;; copyright information from original PCL sources:
11 ;;;;
12 ;;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
13 ;;;; All rights reserved.
14 ;;;;
15 ;;;; Use and copying of this software and preparation of derivative works based
16 ;;;; upon this software are permitted. Any distribution of this software or
17 ;;;; derivative works must comply with all applicable United States export
18 ;;;; control laws.
19 ;;;;
20 ;;;; This software is made available AS IS, and Xerox Corporation makes no
21 ;;;; warranty about the software, its performance or its conformity to any
22 ;;;; specification.
24 (in-package "SB-PCL")
26 ;;;; ANSI CL condition for unbound slots
28 (define-condition unbound-slot (cell-error)
29 ((instance :reader unbound-slot-instance :initarg :instance))
30 (:report (lambda (condition stream)
31 (handler-case
32 (format stream "~@<The slot ~/sb-ext:print-symbol-with-prefix/ ~
33 is unbound in the object ~A.~@:>"
34 (cell-error-name condition)
35 (unbound-slot-instance condition))
36 (serious-condition ()
37 ;; In case of an error try again avoiding custom PRINT-OBJECT's.
38 (format stream "~&Error during printing.~%~@<The slot ~
39 ~/sb-ext:print-symbol-with-prefix/ ~
40 is unbound in an instance of ~
41 ~/sb-ext:print-symbol-with-prefix/.~@:>"
42 (cell-error-name condition)
43 (type-of (unbound-slot-instance condition))))))))
45 (defmethod wrapper-fetcher ((class standard-class))
46 'std-instance-wrapper)
48 (defmethod slots-fetcher ((class standard-class))
49 'std-instance-slots)
51 (defmethod raw-instance-allocator ((class standard-class))
52 'allocate-standard-instance)
54 ;;; These three functions work on std-instances and fsc-instances. These are
55 ;;; instances for which it is possible to change the wrapper and the slots.
56 ;;;
57 ;;; For these kinds of instances, most specified methods from the instance
58 ;;; structure protocol are promoted to the implementation-specific class
59 ;;; std-class. Many of these methods call these four functions.
61 (defun %swap-wrappers-and-slots (i1 i2)
62 (cond ((std-instance-p i1)
63 (let ((w1 (std-instance-wrapper i1))
64 (s1 (std-instance-slots i1)))
65 (setf (std-instance-wrapper i1) (std-instance-wrapper i2))
66 (setf (std-instance-slots i1) (std-instance-slots i2))
67 (setf (std-instance-wrapper i2) w1)
68 (setf (std-instance-slots i2) s1)))
69 ((fsc-instance-p i1)
70 (let ((w1 (fsc-instance-wrapper i1))
71 (s1 (fsc-instance-slots i1)))
72 (setf (fsc-instance-wrapper i1) (fsc-instance-wrapper i2))
73 (setf (fsc-instance-slots i1) (fsc-instance-slots i2))
74 (setf (fsc-instance-wrapper i2) w1)
75 (setf (fsc-instance-slots i2) s1)))
77 (error "unrecognized instance type"))))
79 ;;;; STANDARD-INSTANCE-ACCESS
81 (declaim (inline standard-instance-access
82 (setf standard-instance-access)
83 (cas stadard-instance-access)
84 funcallable-standard-instance-access
85 (setf funcallable-standard-instance-access)
86 (cas funcallable-standard-instance-access)))
88 (defun standard-instance-access (instance location)
89 (clos-slots-ref (std-instance-slots instance) location))
91 (defun (setf standard-instance-access) (new-value instance location)
92 (setf (clos-slots-ref (std-instance-slots instance) location) new-value))
94 (defun (cas standard-instance-access) (old-value new-value instance location)
95 ;; FIXME: Maybe get rid of CLOS-SLOTS-REF entirely?
96 (cas (svref (std-instance-slots instance) location) old-value new-value))
98 (defun funcallable-standard-instance-access (instance location)
99 (clos-slots-ref (fsc-instance-slots instance) location))
101 (defun (setf funcallable-standard-instance-access) (new-value instance location)
102 (setf (clos-slots-ref (fsc-instance-slots instance) location) new-value))
104 (defun (cas funcallable-standard-instance-access) (old-value new-value instance location)
105 ;; FIXME: Maybe get rid of CLOS-SLOTS-REF entirely?
106 (cas (svref (fsc-instance-slots instance) location) old-value new-value))
108 ;;;; SLOT-VALUE, (SETF SLOT-VALUE), SLOT-BOUNDP, SLOT-MAKUNBOUND
110 (declaim (ftype (sfunction (t symbol) t) slot-value))
111 (defun slot-value (object slot-name)
112 (let* ((wrapper (valid-wrapper-of object))
113 (cell (find-slot-cell wrapper slot-name))
114 (location (car cell))
115 (value
116 (cond ((fixnump location)
117 (if (std-instance-p object)
118 (standard-instance-access object location)
119 (funcallable-standard-instance-access object location)))
120 ((not location)
121 (return-from slot-value
122 (if cell
123 (funcall (slot-info-reader (cdr cell)) object)
124 (values (slot-missing (wrapper-class* wrapper) object
125 slot-name 'slot-value)))))
126 ;; this next test means CONSP, but the transform that weakens
127 ;; CONSP to LISTP isn't working here for some reason.
128 ((listp location)
129 (cdr location))
131 (bug "Bogus slot cell in SLOT-VALUE: ~S" cell)))))
132 (if (eq +slot-unbound+ value)
133 (slot-unbound (wrapper-class* wrapper) object slot-name)
134 value)))
136 (defun set-slot-value (object slot-name new-value)
137 (let* ((wrapper (valid-wrapper-of object))
138 (cell (or (find-slot-cell wrapper slot-name)
139 (return-from set-slot-value
140 (progn (slot-missing (wrapper-class* wrapper)
141 object slot-name 'setf new-value)
142 new-value))))
143 (location (car cell))
144 (info (cdr cell))
145 (typecheck (slot-info-typecheck info)))
146 (when typecheck
147 (funcall typecheck new-value))
148 (cond ((fixnump location)
149 (if (std-instance-p object)
150 (setf (standard-instance-access object location) new-value)
151 (setf (funcallable-standard-instance-access object location)
152 new-value)))
153 ((not location)
154 (funcall (slot-info-writer info) new-value object))
155 ((listp location) ; forcibly transform CONSP to LISTP
156 (setf (cdr location) new-value))
158 (bug "Bogus slot-cell in SET-SLOT-VALUE: ~S" cell))))
159 new-value)
161 ;;; A version of SET-SLOT-VALUE for use in safe code, where we want to
162 ;;; check types when writing to slots:
163 ;;; * Doesn't have an optimizing compiler-macro
164 ;;; * Isn't special-cased in WALK-METHOD-LAMBDA
165 (defun safe-set-slot-value (object slot-name new-value)
166 (set-slot-value object slot-name new-value))
168 (defun (cas slot-value) (old-value new-value object slot-name)
169 (let* ((wrapper (valid-wrapper-of object))
170 (cell (or (find-slot-cell wrapper slot-name)
171 (return-from slot-value
172 (values (slot-missing (wrapper-class* wrapper) object slot-name
173 'cas (list old-value new-value))))))
174 (location (car cell))
175 (info (cdr cell))
176 (typecheck (slot-info-typecheck info)))
177 (when typecheck
178 (funcall typecheck new-value))
179 (let ((old (cond ((fixnump location)
180 (if (std-instance-p object)
181 (cas (standard-instance-access object location) old-value new-value)
182 (cas (funcallable-standard-instance-access object location)
183 old-value new-value)))
184 ((not location)
185 ;; FIXME: (CAS SLOT-VALUE-USING-CLASS)...
186 (error "Cannot compare-and-swap slot ~S on: ~S" slot-name object))
187 ((listp location) ; forcibly transform CONSP to LISTP
188 (cas (cdr location) old-value new-value))
190 (bug "Bogus slot-cell in (CAS SLOT-VALUE): ~S" cell)))))
191 (if (and (eq +slot-unbound+ old)
192 (neq old old-value))
193 (slot-unbound (wrapper-class* wrapper) object slot-name)
194 old))))
196 (defun slot-boundp (object slot-name)
197 (let* ((wrapper (valid-wrapper-of object))
198 (cell (find-slot-cell wrapper slot-name))
199 (location (car cell))
200 (value
201 (cond ((fixnump location)
202 (if (std-instance-p object)
203 (standard-instance-access object location)
204 (funcallable-standard-instance-access object location)))
205 ((not location)
206 (return-from slot-boundp
207 (if cell
208 (funcall (slot-info-boundp (cdr cell)) object)
209 (and (slot-missing (wrapper-class* wrapper) object
210 slot-name 'slot-boundp)
211 t))))
212 ((listp location) ; forcibly transform CONSP to LISTP
213 (cdr location))
215 (bug "Bogus slot cell in SLOT-VALUE: ~S" cell)))))
216 (not (eq +slot-unbound+ value))))
218 (defun slot-makunbound (object slot-name)
219 (let* ((wrapper (valid-wrapper-of object))
220 (cell (find-slot-cell wrapper slot-name))
221 (location (car cell)))
222 (cond ((fixnump location)
223 (if (std-instance-p object)
224 (setf (standard-instance-access object location) +slot-unbound+)
225 (setf (funcallable-standard-instance-access object location)
226 +slot-unbound+)))
227 ((not location)
228 (if cell
229 (let ((class (wrapper-class* wrapper)))
230 (slot-makunbound-using-class class object
231 (find-slot-definition class slot-name)))
232 (slot-missing (wrapper-class* wrapper) object slot-name
233 'slot-makunbound)))
234 ((listp location) ; forcibly transform CONSP to LISTP
235 (setf (cdr location) +slot-unbound+))
237 (bug "Bogus slot-cell in SLOT-MAKUNBOUND: ~S" cell))))
238 object)
240 ;; Note that CLHS "encourages" implementors to base this on
241 ;; SLOT-EXISTS-P-USING-CLASS, whereas 88-002R made no such claim,
242 ;; however Appendix D of AMOP sketches out such an implementation.
243 (defun slot-exists-p (object slot-name)
244 (not (null (find-slot-cell (valid-wrapper-of object) slot-name))))
246 (defun slot-value-for-printing (object slot-name)
247 (if (slot-boundp object slot-name)
248 (slot-value object slot-name)
249 (load-time-value (make-unprintable-object "unbound slot") t)))
251 (defmethod slot-value-using-class ((class std-class)
252 (object standard-object)
253 (slotd standard-effective-slot-definition))
254 ;; FIXME: Do we need this? SLOT-VALUE checks for obsolete
255 ;; instances. Are users allowed to call this directly?
256 (check-obsolete-instance object)
257 (let* ((location (slot-definition-location slotd))
258 (value
259 (typecase location
260 (fixnum
261 (cond ((std-instance-p object)
262 (clos-slots-ref (std-instance-slots object)
263 location))
264 ((fsc-instance-p object)
265 (clos-slots-ref (fsc-instance-slots object)
266 location))
267 (t (bug "unrecognized instance type in ~S"
268 'slot-value-using-class))))
269 (cons
270 (cdr location))
272 (instance-structure-protocol-error slotd
273 'slot-value-using-class)))))
274 (if (eq value +slot-unbound+)
275 (values (slot-unbound class object (slot-definition-name slotd)))
276 value)))
278 (defmethod (setf slot-value-using-class)
279 (new-value (class std-class)
280 (object standard-object)
281 (slotd standard-effective-slot-definition))
282 ;; FIXME: Do we need this? SET-SLOT-VALUE checks for obsolete
283 ;; instances. Are users allowed to call this directly?
284 (check-obsolete-instance object)
285 (let* ((info (slot-definition-info slotd))
286 (location (slot-definition-location slotd))
287 (typecheck (slot-info-typecheck info))
288 (new-value (if typecheck
289 (funcall (the function typecheck) new-value)
290 new-value)))
291 (typecase location
292 (fixnum
293 (cond ((std-instance-p object)
294 (setf (clos-slots-ref (std-instance-slots object) location)
295 new-value))
296 ((fsc-instance-p object)
297 (setf (clos-slots-ref (fsc-instance-slots object) location)
298 new-value))
299 (t (bug "unrecognized instance type in ~S"
300 '(setf slot-value-using-class)))))
301 (cons
302 (setf (cdr location) new-value))
304 (instance-structure-protocol-error
305 slotd '(setf slot-value-using-class))))))
307 (defmethod slot-boundp-using-class
308 ((class std-class)
309 (object standard-object)
310 (slotd standard-effective-slot-definition))
311 ;; FIXME: Do we need this? SLOT-BOUNDP checks for obsolete
312 ;; instances. Are users allowed to call this directly?
313 (check-obsolete-instance object)
314 (let* ((location (slot-definition-location slotd))
315 (value
316 (typecase location
317 (fixnum
318 (cond ((std-instance-p object)
319 (clos-slots-ref (std-instance-slots object)
320 location))
321 ((fsc-instance-p object)
322 (clos-slots-ref (fsc-instance-slots object)
323 location))
324 (t (bug "unrecognized instance type in ~S"
325 'slot-boundp-using-class))))
326 (cons
327 (cdr location))
329 (instance-structure-protocol-error slotd
330 'slot-boundp-using-class)))))
331 (not (eq value +slot-unbound+))))
333 (defmethod slot-makunbound-using-class
334 ((class std-class)
335 (object standard-object)
336 (slotd standard-effective-slot-definition))
337 (check-obsolete-instance object)
338 (let ((location (slot-definition-location slotd)))
339 (typecase location
340 (fixnum
341 (cond ((std-instance-p object)
342 (setf (clos-slots-ref (std-instance-slots object) location)
343 +slot-unbound+))
344 ((fsc-instance-p object)
345 (setf (clos-slots-ref (fsc-instance-slots object) location)
346 +slot-unbound+))
347 (t (bug "unrecognized instance type in ~S"
348 'slot-makunbound-using-class))))
349 (cons
350 (setf (cdr location) +slot-unbound+))
352 (instance-structure-protocol-error slotd
353 'slot-makunbound-using-class))))
354 object)
356 (defmethod slot-value-using-class
357 ((class condition-class)
358 (object condition)
359 (slotd condition-effective-slot-definition))
360 (let ((fun (slot-info-reader (slot-definition-info slotd))))
361 (funcall fun object)))
363 (defmethod (setf slot-value-using-class)
364 (new-value
365 (class condition-class)
366 (object condition)
367 (slotd condition-effective-slot-definition))
368 (let ((fun (slot-info-writer (slot-definition-info slotd))))
369 (funcall fun new-value object)))
371 (defmethod slot-boundp-using-class
372 ((class condition-class)
373 (object condition)
374 (slotd condition-effective-slot-definition))
375 (let ((fun (slot-info-boundp (slot-definition-info slotd))))
376 (funcall fun object)))
378 (defmethod slot-makunbound-using-class ((class condition-class) object slot)
379 (error "attempt to unbind slot ~S in condition object ~S."
380 slot object))
382 (defmethod slot-value-using-class
383 ((class structure-class)
384 (object structure-object)
385 (slotd structure-effective-slot-definition))
386 (let* ((function (slot-definition-internal-reader-function slotd))
387 (value (funcall function object)))
388 (declare (type function function))
389 ;; FIXME: Is this really necessary? Structure slots should surely
390 ;; never be unbound!
391 (if (eq value +slot-unbound+)
392 (values (slot-unbound class object (slot-definition-name slotd)))
393 value)))
395 (defmethod (setf slot-value-using-class)
396 (new-value (class structure-class)
397 (object structure-object)
398 (slotd structure-effective-slot-definition))
399 (let ((function (slot-definition-internal-writer-function slotd)))
400 (declare (type function function))
401 (funcall function new-value object)))
403 (defmethod slot-boundp-using-class
404 ((class structure-class)
405 (object structure-object)
406 (slotd structure-effective-slot-definition))
409 (defmethod slot-makunbound-using-class
410 ((class structure-class)
411 (object structure-object)
412 (slotd structure-effective-slot-definition))
413 (error "Structure slots can't be unbound."))
415 (defmethod slot-missing
416 ((class t) instance slot-name operation &optional new-value)
417 (error "~@<When attempting to ~A, the slot ~S is missing from the ~
418 object ~S.~@:>"
419 (ecase operation
420 (slot-value "read the slot's value (slot-value)")
421 (setf (format nil
422 "set the slot's value to ~S (SETF of SLOT-VALUE)"
423 new-value))
424 (slot-boundp "test to see whether slot is bound (SLOT-BOUNDP)")
425 (slot-makunbound "make the slot unbound (SLOT-MAKUNBOUND)"))
426 slot-name
427 instance))
429 (defmethod slot-unbound ((class t) instance slot-name)
430 (restart-case
431 (error 'unbound-slot :name slot-name :instance instance)
432 (use-value (v)
433 :report "Return a value as the slot-value."
434 :interactive read-evaluated-form
436 (store-value (v)
437 :report "Store and return a value as the slot-value."
438 :interactive read-evaluated-form
439 (setf (slot-value instance slot-name) v))))
441 (defun slot-unbound-internal (instance position)
442 (values
443 (slot-unbound
444 (class-of instance)
445 instance
446 (etypecase position
447 (fixnum
448 ;; In the vast majority of cases location corresponds to the position
449 ;; in list. The only exceptions are when there are non-local slots
450 ;; before the one we want.
451 (let* ((slots (layout-slot-list (layout-of instance)))
452 (guess (nth position slots)))
453 (if (eql position (slot-definition-location guess))
454 (slot-definition-name guess)
455 (slot-definition-name
456 (car (member position (class-slots instance) :key #'slot-definition-location))))))
457 (cons
458 (car position))))))
460 ;;; FIXME: AMOP says that allocate-instance implies finalize-inheritance
461 ;;; if the class is not yet finalized, but we don't seem to be taking
462 ;;; care of this for non-standard-classes.
463 (defmethod allocate-instance ((class standard-class) &rest initargs)
464 (declare (ignore initargs)
465 (inline ensure-class-finalized))
466 (allocate-standard-instance
467 (class-wrapper (ensure-class-finalized class))))
469 (defmethod allocate-instance ((class structure-class) &rest initargs)
470 (declare (ignore initargs))
471 (let ((constructor (class-defstruct-constructor class)))
472 (if constructor
473 (funcall constructor)
474 (error "Don't know how to allocate ~S" class))))
476 (defmethod allocate-instance ((class condition-class) &rest initargs)
477 (declare (ignore initargs))
478 (values (allocate-condition (class-name class))))
480 (defmethod allocate-instance ((class system-class) &rest initargs)
481 (declare (ignore initargs))
482 (error "Cannot allocate an instance of ~S." class))
484 ;;; AMOP says that CLASS-SLOTS signals an error for unfinalized classes.
485 (defmethod class-slots :before ((class slot-class))
486 (unless (class-finalized-p class)
487 (error 'simple-reference-error
488 :format-control "~S called on ~S, which is not yet finalized."
489 :format-arguments (list 'class-slots class)
490 :references (list '(:amop :generic-function class-slots)))))
492 (defun %set-slots (object names &rest values)
493 (mapc (lambda (name value)
494 (if (eq value +slot-unbound+)
495 ;; SLOT-MAKUNBOUND-USING-CLASS might do something nonstandard.
496 (slot-makunbound object name)
497 (setf (slot-value object name) value)))
498 names values))