signal errors on trying to subclass BUILT-IN-CLASSes, lp#861004
[sbcl.git] / src / pcl / slots.lisp
blob0344e757cdb79c7b941eca27cf37974b3b803c82
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 (or (find-slot-cell wrapper slot-name)
114 (return-from slot-value
115 (values (slot-missing (wrapper-class* wrapper) object slot-name
116 'slot-value)))))
117 (location (car cell))
118 (value
119 (cond ((fixnump location)
120 (if (std-instance-p object)
121 (standard-instance-access object location)
122 (funcallable-standard-instance-access object location)))
123 ((consp location)
124 (cdr location))
125 ((not location)
126 (return-from slot-value
127 (funcall (slot-info-reader (cdr cell)) object)))
129 (bug "Bogus slot cell in SLOT-VALUE: ~S" cell)))))
130 (if (eq +slot-unbound+ value)
131 (slot-unbound (wrapper-class* wrapper) object slot-name)
132 value)))
134 ;;; This is used during the PCL build, but gets replaced by a deftransform
135 ;;; in fixup.lisp.
136 (define-compiler-macro slot-value (&whole form object slot-name
137 &environment env)
138 (if (and (constantp slot-name env)
139 (interned-symbol-p (constant-form-value slot-name env)))
140 `(accessor-slot-value ,object ,slot-name)
141 form))
143 (defun set-slot-value (object slot-name new-value)
144 (let* ((wrapper (valid-wrapper-of object))
145 (cell (or (find-slot-cell wrapper slot-name)
146 (return-from set-slot-value
147 (values (slot-missing (wrapper-class* wrapper) object slot-name
148 'setf new-value)))))
149 (location (car cell))
150 (info (cdr cell))
151 (typecheck (slot-info-typecheck info)))
152 (when typecheck
153 (funcall typecheck new-value))
154 (cond ((fixnump location)
155 (if (std-instance-p object)
156 (setf (standard-instance-access object location) new-value)
157 (setf (funcallable-standard-instance-access object location)
158 new-value)))
159 ((consp location)
160 (setf (cdr location) new-value))
161 ((not location)
162 (funcall (slot-info-writer info) new-value object))
164 (bug "Bogus slot-cell in SET-SLOT-VALUE: ~S" cell))))
165 new-value)
167 ;;; A version of SET-SLOT-VALUE for use in safe code, where we want to
168 ;;; check types when writing to slots:
169 ;;; * Doesn't have an optimizing compiler-macro
170 ;;; * Isn't special-cased in WALK-METHOD-LAMBDA
171 (defun safe-set-slot-value (object slot-name new-value)
172 (set-slot-value object slot-name new-value))
174 ;;; This is used during the PCL build, but gets replaced by a deftransform
175 ;;; in fixup.lisp.
176 (define-compiler-macro set-slot-value (&whole form object slot-name new-value
177 &environment env)
178 (if (and (constantp slot-name env)
179 (interned-symbol-p (constant-form-value slot-name env))
180 ;; We can't use the ACCESSOR-SET-SLOT-VALUE path in safe
181 ;; code, since it'll use the global automatically generated
182 ;; accessor, which won't do typechecking. (SLOT-OBJECT
183 ;; won't have been compiled with SAFETY 3, so SAFE-P will
184 ;; be NIL in MAKE-STD-WRITER-METHOD-FUNCTION).
185 (not (safe-code-p env)))
186 `(accessor-set-slot-value ,object ,slot-name ,new-value)
187 form))
189 (defun (cas slot-value) (old-value new-value object slot-name)
190 (let* ((wrapper (valid-wrapper-of object))
191 (cell (or (find-slot-cell wrapper slot-name)
192 (return-from slot-value
193 (values (slot-missing (wrapper-class* wrapper) object slot-name
194 'cas (list old-value new-value))))))
195 (location (car cell))
196 (info (cdr cell))
197 (typecheck (slot-info-typecheck info)))
198 (when typecheck
199 (funcall typecheck new-value))
200 (let ((old (cond ((fixnump location)
201 (if (std-instance-p object)
202 (cas (standard-instance-access object location) old-value new-value)
203 (cas (funcallable-standard-instance-access object location)
204 old-value new-value)))
205 ((consp location)
206 (cas (cdr location) old-value new-value))
207 ((not location)
208 ;; FIXME: (CAS SLOT-VALUE-USING-CLASS)...
209 (error "Cannot compare-and-swap slot ~S on: ~S" slot-name object))
211 (bug "Bogus slot-cell in (CAS SLOT-VALUE): ~S" cell)))))
212 (if (and (eq +slot-unbound+ old)
213 (neq old old-value))
214 (slot-unbound (wrapper-class* wrapper) object slot-name)
215 old))))
217 (defun slot-boundp (object slot-name)
218 (let* ((wrapper (valid-wrapper-of object))
219 (cell (or (find-slot-cell wrapper slot-name)
220 (return-from slot-boundp
221 (and (slot-missing (wrapper-class* wrapper) object slot-name
222 'slot-boundp)
223 t))))
224 (location (car cell))
225 (value
226 (cond ((fixnump location)
227 (if (std-instance-p object)
228 (standard-instance-access object location)
229 (funcallable-standard-instance-access object location)))
230 ((consp location)
231 (cdr location))
232 ((not location)
233 (return-from slot-boundp
234 (funcall (slot-info-boundp (cdr cell)) object)))
236 (bug "Bogus slot cell in SLOT-VALUE: ~S" cell)))))
237 (not (eq +slot-unbound+ value))))
239 (define-compiler-macro slot-boundp (&whole form object slot-name
240 &environment env)
241 (if (and (constantp slot-name env)
242 (interned-symbol-p (constant-form-value slot-name env)))
243 `(accessor-slot-boundp ,object ,slot-name)
244 form))
246 (defun slot-makunbound (object slot-name)
247 (let* ((wrapper (valid-wrapper-of object))
248 (cell (find-slot-cell wrapper slot-name))
249 (location (car cell)))
250 (cond ((fixnump location)
251 (if (std-instance-p object)
252 (setf (standard-instance-access object location) +slot-unbound+)
253 (setf (funcallable-standard-instance-access object location)
254 +slot-unbound+)))
255 ((consp location)
256 (setf (cdr location) +slot-unbound+))
257 ((not cell)
258 (slot-missing (wrapper-class* wrapper) object slot-name 'slot-makunbound))
259 ((not location)
260 (let ((class (wrapper-class* wrapper)))
261 (slot-makunbound-using-class class object (find-slot-definition class slot-name))))
263 (bug "Bogus slot-cell in SLOT-MAKUNBOUND: ~S" cell))))
264 object)
266 (defun slot-exists-p (object slot-name)
267 (let ((class (class-of object)))
268 (not (null (find-slot-definition class slot-name)))))
270 (defvar *unbound-slot-value-marker* (make-unprintable-object "unbound slot"))
272 ;;; This isn't documented, but is used within PCL in a number of print
273 ;;; object methods. (See NAMED-OBJECT-PRINT-FUNCTION.)
274 (defun slot-value-or-default (object slot-name &optional
275 (default *unbound-slot-value-marker*))
276 (if (slot-boundp object slot-name)
277 (slot-value object slot-name)
278 default))
280 (defmethod slot-value-using-class ((class std-class)
281 (object standard-object)
282 (slotd standard-effective-slot-definition))
283 ;; FIXME: Do we need this? SLOT-VALUE checks for obsolete
284 ;; instances. Are users allowed to call this directly?
285 (check-obsolete-instance object)
286 (let* ((location (slot-definition-location slotd))
287 (value
288 (typecase location
289 (fixnum
290 (cond ((std-instance-p object)
291 (clos-slots-ref (std-instance-slots object)
292 location))
293 ((fsc-instance-p object)
294 (clos-slots-ref (fsc-instance-slots object)
295 location))
296 (t (bug "unrecognized instance type in ~S"
297 'slot-value-using-class))))
298 (cons
299 (cdr location))
301 (instance-structure-protocol-error slotd
302 'slot-value-using-class)))))
303 (if (eq value +slot-unbound+)
304 (values (slot-unbound class object (slot-definition-name slotd)))
305 value)))
307 (defmethod (setf slot-value-using-class)
308 (new-value (class std-class)
309 (object standard-object)
310 (slotd standard-effective-slot-definition))
311 ;; FIXME: Do we need this? SET-SLOT-VALUE checks for obsolete
312 ;; instances. Are users allowed to call this directly?
313 (check-obsolete-instance object)
314 (let* ((info (slot-definition-info slotd))
315 (location (slot-definition-location slotd))
316 (typecheck (slot-info-typecheck info))
317 (new-value (if typecheck
318 (funcall (the function typecheck) new-value)
319 new-value)))
320 (typecase location
321 (fixnum
322 (cond ((std-instance-p object)
323 (setf (clos-slots-ref (std-instance-slots object) location)
324 new-value))
325 ((fsc-instance-p object)
326 (setf (clos-slots-ref (fsc-instance-slots object) location)
327 new-value))
328 (t (bug "unrecognized instance type in ~S"
329 '(setf slot-value-using-class)))))
330 (cons
331 (setf (cdr location) new-value))
333 (instance-structure-protocol-error
334 slotd '(setf slot-value-using-class))))))
336 (defmethod slot-boundp-using-class
337 ((class std-class)
338 (object standard-object)
339 (slotd standard-effective-slot-definition))
340 ;; FIXME: Do we need this? SLOT-BOUNDP checks for obsolete
341 ;; instances. Are users allowed to call this directly?
342 (check-obsolete-instance object)
343 (let* ((location (slot-definition-location slotd))
344 (value
345 (typecase location
346 (fixnum
347 (cond ((std-instance-p object)
348 (clos-slots-ref (std-instance-slots object)
349 location))
350 ((fsc-instance-p object)
351 (clos-slots-ref (fsc-instance-slots object)
352 location))
353 (t (bug "unrecognized instance type in ~S"
354 'slot-boundp-using-class))))
355 (cons
356 (cdr location))
358 (instance-structure-protocol-error slotd
359 'slot-boundp-using-class)))))
360 (not (eq value +slot-unbound+))))
362 (defmethod slot-makunbound-using-class
363 ((class std-class)
364 (object standard-object)
365 (slotd standard-effective-slot-definition))
366 (check-obsolete-instance object)
367 (let ((location (slot-definition-location slotd)))
368 (typecase location
369 (fixnum
370 (cond ((std-instance-p object)
371 (setf (clos-slots-ref (std-instance-slots object) location)
372 +slot-unbound+))
373 ((fsc-instance-p object)
374 (setf (clos-slots-ref (fsc-instance-slots object) location)
375 +slot-unbound+))
376 (t (bug "unrecognized instance type in ~S"
377 'slot-makunbound-using-class))))
378 (cons
379 (setf (cdr location) +slot-unbound+))
381 (instance-structure-protocol-error slotd
382 'slot-makunbound-using-class))))
383 object)
385 (defmethod slot-value-using-class
386 ((class condition-class)
387 (object condition)
388 (slotd condition-effective-slot-definition))
389 (let ((fun (slot-info-reader (slot-definition-info slotd))))
390 (funcall fun object)))
392 (defmethod (setf slot-value-using-class)
393 (new-value
394 (class condition-class)
395 (object condition)
396 (slotd condition-effective-slot-definition))
397 (let ((fun (slot-info-writer (slot-definition-info slotd))))
398 (funcall fun new-value object)))
400 (defmethod slot-boundp-using-class
401 ((class condition-class)
402 (object condition)
403 (slotd condition-effective-slot-definition))
404 (let ((fun (slot-info-boundp (slot-definition-info slotd))))
405 (funcall fun object)))
407 (defmethod slot-makunbound-using-class ((class condition-class) object slot)
408 (error "attempt to unbind slot ~S in condition object ~S."
409 slot object))
411 (defmethod slot-value-using-class
412 ((class structure-class)
413 (object structure-object)
414 (slotd structure-effective-slot-definition))
415 (let* ((function (slot-definition-internal-reader-function slotd))
416 (value (funcall function object)))
417 (declare (type function function))
418 ;; FIXME: Is this really necessary? Structure slots should surely
419 ;; never be unbound!
420 (if (eq value +slot-unbound+)
421 (values (slot-unbound class object (slot-definition-name slotd)))
422 value)))
424 (defmethod (setf slot-value-using-class)
425 (new-value (class structure-class)
426 (object structure-object)
427 (slotd structure-effective-slot-definition))
428 (let ((function (slot-definition-internal-writer-function slotd)))
429 (declare (type function function))
430 (funcall function new-value object)))
432 (defmethod slot-boundp-using-class
433 ((class structure-class)
434 (object structure-object)
435 (slotd structure-effective-slot-definition))
438 (defmethod slot-makunbound-using-class
439 ((class structure-class)
440 (object structure-object)
441 (slotd structure-effective-slot-definition))
442 (error "Structure slots can't be unbound."))
444 (defmethod slot-missing
445 ((class t) instance slot-name operation &optional new-value)
446 (error "~@<When attempting to ~A, the slot ~S is missing from the ~
447 object ~S.~@:>"
448 (ecase operation
449 (slot-value "read the slot's value (slot-value)")
450 (setf (format nil
451 "set the slot's value to ~S (SETF of SLOT-VALUE)"
452 new-value))
453 (slot-boundp "test to see whether slot is bound (SLOT-BOUNDP)")
454 (slot-makunbound "make the slot unbound (SLOT-MAKUNBOUND)"))
455 slot-name
456 instance))
458 (defmethod slot-unbound ((class t) instance slot-name)
459 (restart-case
460 (error 'unbound-slot :name slot-name :instance instance)
461 (use-value (v)
462 :report "Return a value as the slot-value."
463 :interactive read-evaluated-form
465 (store-value (v)
466 :report "Store and return a value as the slot-value."
467 :interactive read-evaluated-form
468 (setf (slot-value instance slot-name) v))))
470 (defun slot-unbound-internal (instance position)
471 (values
472 (slot-unbound
473 (class-of instance)
474 instance
475 (etypecase position
476 (fixnum
477 ;; In the vast majority of cases location corresponds to the position
478 ;; in list. The only exceptions are when there are non-local slots
479 ;; before the one we want.
480 (let* ((slots (layout-slot-list (layout-of instance)))
481 (guess (nth position slots)))
482 (if (eql position (slot-definition-location guess))
483 (slot-definition-name guess)
484 (slot-definition-name
485 (car (member position (class-slots instance) :key #'slot-definition-location))))))
486 (cons
487 (car position))))))
489 ;;; FIXME: AMOP says that allocate-instance imples finalize-inheritance
490 ;;; if the class is not yet finalized, but we don't seem to be taking
491 ;;; care of this for non-standard-classes.
492 (defmethod allocate-instance ((class standard-class) &rest initargs)
493 (declare (ignore initargs))
494 (unless (class-finalized-p class)
495 (finalize-inheritance class))
496 (allocate-standard-instance (class-wrapper class)))
498 (defmethod allocate-instance ((class structure-class) &rest initargs)
499 (declare (ignore initargs))
500 (let ((constructor (class-defstruct-constructor class)))
501 (if constructor
502 (funcall constructor)
503 (error "Don't know how to allocate ~S" class))))
505 (defmethod allocate-instance ((class condition-class) &rest initargs)
506 (declare (ignore initargs))
507 (allocate-condition (class-name class)))
509 (macrolet ((def (name class)
510 `(defmethod ,name ((class ,class) &rest initargs)
511 (declare (ignore initargs))
512 (error "Cannot allocate an instance of ~S." class))))
513 (def allocate-instance system-class))
515 ;;; AMOP says that CLASS-SLOTS signals an error for unfinalized classes.
516 (defmethod class-slots :before ((class slot-class))
517 (unless (class-finalized-p class)
518 (error 'simple-reference-error
519 :format-control "~S called on ~S, which is not yet finalized."
520 :format-arguments (list 'class-slots class)
521 :references (list '(:amop :generic-function class-slots)))))