1.0.23.62: fix bug 357
[sbcl.git] / src / pcl / slots.lisp
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
3
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.
9
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.
23
24 (in-package "SB-PCL")
25 \f
26 ;;;; ANSI CL condition for unbound slots
27
28 (define-condition unbound-slot (cell-error)
29   ((instance :reader unbound-slot-instance :initarg :instance))
30   (:report (lambda (condition stream)
31              (format stream "The slot ~S is unbound in the object ~S."
32                      (cell-error-name condition)
33                      (unbound-slot-instance condition)))))
34
35 (defmethod wrapper-fetcher ((class standard-class))
36   'std-instance-wrapper)
37
38 (defmethod slots-fetcher ((class standard-class))
39   'std-instance-slots)
40
41 (defmethod raw-instance-allocator ((class standard-class))
42   'allocate-standard-instance)
43
44 ;;; These three functions work on std-instances and fsc-instances. These are
45 ;;; instances for which it is possible to change the wrapper and the slots.
46 ;;;
47 ;;; For these kinds of instances, most specified methods from the instance
48 ;;; structure protocol are promoted to the implementation-specific class
49 ;;; std-class. Many of these methods call these four functions.
50
51 (defun %swap-wrappers-and-slots (i1 i2)
52   (cond ((std-instance-p i1)
53          (let ((w1 (std-instance-wrapper i1))
54                (s1 (std-instance-slots i1)))
55            (setf (std-instance-wrapper i1) (std-instance-wrapper i2))
56            (setf (std-instance-slots i1) (std-instance-slots i2))
57            (setf (std-instance-wrapper i2) w1)
58            (setf (std-instance-slots i2) s1)))
59         ((fsc-instance-p i1)
60          (let ((w1 (fsc-instance-wrapper i1))
61                (s1 (fsc-instance-slots i1)))
62            (setf (fsc-instance-wrapper i1) (fsc-instance-wrapper i2))
63            (setf (fsc-instance-slots i1) (fsc-instance-slots i2))
64            (setf (fsc-instance-wrapper i2) w1)
65            (setf (fsc-instance-slots i2) s1)))
66         (t
67          (error "unrecognized instance type"))))
68 \f
69 ;;;; STANDARD-INSTANCE-ACCESS
70
71 (declaim (inline standard-instance-access (setf standard-instance-access)
72                  funcallable-standard-instance-access
73                  (setf funcallable-standard-instance-access)))
74
75 (defun standard-instance-access (instance location)
76   (clos-slots-ref (std-instance-slots instance) location))
77
78 (defun (setf standard-instance-access) (new-value instance location)
79   (setf (clos-slots-ref (std-instance-slots instance) location) new-value))
80
81 (defun funcallable-standard-instance-access (instance location)
82   (clos-slots-ref (fsc-instance-slots instance) location))
83
84 (defun (setf funcallable-standard-instance-access) (new-value instance location)
85   (setf (clos-slots-ref (fsc-instance-slots instance) location) new-value))
86 \f
87 ;;;; SLOT-VALUE, (SETF SLOT-VALUE), SLOT-BOUNDP, SLOT-MAKUNBOUND
88
89 (declaim (ftype (sfunction (t symbol) t) slot-value))
90 (defun slot-value (object slot-name)
91   (let* ((wrapper (valid-wrapper-of object))
92          (cell (find-slot-cell wrapper slot-name))
93          (location (car cell))
94          (value
95           (cond ((fixnump location)
96                  (if (std-instance-p object)
97                      (standard-instance-access object location)
98                      (funcallable-standard-instance-access object location)))
99                 ((consp location)
100                  (cdr location))
101                 ((not cell)
102                  (return-from slot-value
103                    (values (slot-missing (wrapper-class* wrapper) object slot-name
104                                          'slot-value))))
105                 ((not location)
106                  (return-from slot-value
107                    (slot-value-using-class (wrapper-class* wrapper) object (cddr cell))))
108                 (t
109                  (bug "Bogus slot cell in SLOT-VALUE: ~S" cell)))))
110     (if (eq +slot-unbound+ value)
111         (slot-unbound (wrapper-class* wrapper) object slot-name)
112         value)))
113
114 (define-compiler-macro slot-value (&whole form object slot-name
115                                    &environment env)
116   (if (and (constantp slot-name env)
117            (interned-symbol-p (constant-form-value slot-name env)))
118       `(accessor-slot-value ,object ,slot-name)
119       form))
120
121 (defun set-slot-value (object slot-name new-value)
122   (let* ((wrapper (valid-wrapper-of object))
123          (cell (find-slot-cell wrapper slot-name))
124          (location (car cell))
125          (type-check-function (cadr cell)))
126     (when type-check-function
127       (funcall (the function type-check-function) new-value))
128     (cond ((fixnump location)
129            (if (std-instance-p object)
130                (setf (standard-instance-access object location) new-value)
131                (setf (funcallable-standard-instance-access object location)
132                      new-value)))
133           ((consp location)
134            (setf (cdr location) new-value))
135           ((not cell)
136            (slot-missing (wrapper-class* wrapper) object slot-name 'setf new-value))
137           ((not location)
138            (setf (slot-value-using-class (wrapper-class* wrapper) object (cddr cell))
139                  new-value))
140           (t
141            (bug "Bogus slot-cell in SET-SLOT-VALUE: ~S" cell))))
142   new-value)
143
144 ;;; A version of SET-SLOT-VALUE for use in safe code, where we want to
145 ;;; check types when writing to slots:
146 ;;;   * Doesn't have an optimizing compiler-macro
147 ;;;   * Isn't special-cased in WALK-METHOD-LAMBDA
148 (defun safe-set-slot-value (object slot-name new-value)
149   (set-slot-value object slot-name new-value))
150
151 (define-compiler-macro set-slot-value (&whole form object slot-name new-value
152                                       &environment env)
153   (if (and (constantp slot-name env)
154            (interned-symbol-p (constant-form-value slot-name env))
155            ;; We can't use the ACCESSOR-SET-SLOT-VALUE path in safe
156            ;; code, since it'll use the global automatically generated
157            ;; accessor, which won't do typechecking. (SLOT-OBJECT
158            ;; won't have been compiled with SAFETY 3, so SAFE-P will
159            ;; be NIL in MAKE-STD-WRITER-METHOD-FUNCTION).
160            (not (safe-code-p env)))
161       `(accessor-set-slot-value ,object ,slot-name ,new-value)
162       form))
163
164 (defun slot-boundp (object slot-name)
165   (let* ((wrapper (valid-wrapper-of object))
166          (cell (find-slot-cell wrapper slot-name))
167          (location (car cell))
168          (value
169           (cond ((fixnump location)
170                  (if (std-instance-p object)
171                      (standard-instance-access object location)
172                      (funcallable-standard-instance-access object location)))
173                 ((consp location)
174                  (cdr location))
175                 ((not cell)
176                  (return-from slot-boundp
177                    (and (slot-missing (wrapper-class* wrapper) object slot-name
178                                       'slot-boundp)
179                         t)))
180                 ((not location)
181                  (return-from slot-boundp
182                    (slot-boundp-using-class (wrapper-class* wrapper) object (cddr cell))))
183                 (t
184                  (bug "Bogus slot cell in SLOT-VALUE: ~S" cell)))))
185     (not (eq +slot-unbound+ value))))
186
187 (define-compiler-macro slot-boundp (&whole form object slot-name
188                                     &environment env)
189   (if (and (constantp slot-name env)
190            (interned-symbol-p (constant-form-value slot-name env)))
191       `(accessor-slot-boundp ,object ,slot-name)
192       form))
193
194 (defun slot-makunbound (object slot-name)
195   (let* ((wrapper (valid-wrapper-of object))
196          (cell (find-slot-cell wrapper slot-name))
197          (location (car cell)))
198     (cond ((fixnump location)
199            (if (std-instance-p object)
200                (setf (standard-instance-access object location) +slot-unbound+)
201                (setf (funcallable-standard-instance-access object location)
202                      +slot-unbound+)))
203           ((consp location)
204            (setf (cdr location) +slot-unbound+))
205           ((not cell)
206            (slot-missing (wrapper-class* wrapper) object slot-name 'slot-makunbound))
207           ((not location)
208            (slot-makunbound-using-class (wrapper-class* wrapper) object (cddr cell)))
209           (t
210            (bug "Bogus slot-cell in SLOT-MAKUNBOUND: ~S" cell))))
211   object)
212
213 (defun slot-exists-p (object slot-name)
214   (let ((class (class-of object)))
215     (not (null (find-slot-definition class slot-name)))))
216
217 (defvar *unbound-slot-value-marker* (make-unprintable-object "unbound slot"))
218
219 ;;; This isn't documented, but is used within PCL in a number of print
220 ;;; object methods. (See NAMED-OBJECT-PRINT-FUNCTION.)
221 (defun slot-value-or-default (object slot-name &optional
222                               (default *unbound-slot-value-marker*))
223   (if (slot-boundp object slot-name)
224       (slot-value object slot-name)
225       default))
226
227 (defmethod slot-value-using-class ((class std-class)
228                                    (object standard-object)
229                                    (slotd standard-effective-slot-definition))
230   ;; FIXME: Do we need this? SLOT-VALUE checks for obsolete
231   ;; instances. Are users allowed to call this directly?
232   (check-obsolete-instance object)
233   (let* ((location (slot-definition-location slotd))
234          (value
235           (typecase location
236             (fixnum
237              (cond ((std-instance-p object)
238                     (clos-slots-ref (std-instance-slots object)
239                                     location))
240                    ((fsc-instance-p object)
241                     (clos-slots-ref (fsc-instance-slots object)
242                                     location))
243                    (t (bug "unrecognized instance type in ~S"
244                            'slot-value-using-class))))
245             (cons
246              (cdr location))
247             (t
248              (instance-structure-protocol-error slotd
249                                                 'slot-value-using-class)))))
250     (if (eq value +slot-unbound+)
251         (values (slot-unbound class object (slot-definition-name slotd)))
252         value)))
253
254 (defmethod (setf slot-value-using-class)
255            (new-value (class std-class)
256                       (object standard-object)
257                       (slotd standard-effective-slot-definition))
258   ;; FIXME: Do we need this? SET-SLOT-VALUE checks for obsolete
259   ;; instances. Are users allowed to call this directly?
260   (check-obsolete-instance object)
261   (let ((location (slot-definition-location slotd))
262         (type-check-function
263          (when (safe-p class)
264            (slot-definition-type-check-function slotd))))
265     (flet ((check (new-value)
266              (when type-check-function
267                (funcall (the function type-check-function) new-value))
268              new-value))
269       (typecase location
270         (fixnum
271          (cond ((std-instance-p object)
272                 (setf (clos-slots-ref (std-instance-slots object) location)
273                       (check new-value)))
274                ((fsc-instance-p object)
275                 (setf (clos-slots-ref (fsc-instance-slots object) location)
276                       (check new-value)))
277                 (t (bug "unrecognized instance type in ~S"
278                         '(setf slot-value-using-class)))))
279         (cons
280          (setf (cdr location) (check new-value)))
281         (t
282          (instance-structure-protocol-error
283           slotd '(setf slot-value-using-class)))))))
284
285 (defmethod slot-boundp-using-class
286            ((class std-class)
287             (object standard-object)
288             (slotd standard-effective-slot-definition))
289   ;; FIXME: Do we need this? SLOT-BOUNDP checks for obsolete
290   ;; instances. Are users allowed to call this directly?
291   (check-obsolete-instance object)
292   (let* ((location (slot-definition-location slotd))
293          (value
294           (typecase location
295             (fixnum
296              (cond ((std-instance-p object)
297                           (clos-slots-ref (std-instance-slots object)
298                                           location))
299                    ((fsc-instance-p object)
300                     (clos-slots-ref (fsc-instance-slots object)
301                                     location))
302                    (t (bug "unrecognized instance type in ~S"
303                            'slot-boundp-using-class))))
304             (cons
305              (cdr location))
306             (t
307              (instance-structure-protocol-error slotd
308                                                 'slot-boundp-using-class)))))
309     (not (eq value +slot-unbound+))))
310
311 (defmethod slot-makunbound-using-class
312            ((class std-class)
313             (object standard-object)
314             (slotd standard-effective-slot-definition))
315   (check-obsolete-instance object)
316   (let ((location (slot-definition-location slotd)))
317     (typecase location
318       (fixnum
319        (cond ((std-instance-p object)
320               (setf (clos-slots-ref (std-instance-slots object) location)
321                     +slot-unbound+))
322              ((fsc-instance-p object)
323               (setf (clos-slots-ref (fsc-instance-slots object) location)
324                     +slot-unbound+))
325              (t (bug "unrecognized instance type in ~S"
326                      'slot-makunbound-using-class))))
327       (cons
328        (setf (cdr location) +slot-unbound+))
329       (t
330        (instance-structure-protocol-error slotd
331                                           'slot-makunbound-using-class))))
332   object)
333
334 (defmethod slot-value-using-class
335     ((class condition-class)
336      (object condition)
337      (slotd condition-effective-slot-definition))
338   (let ((fun (slot-definition-reader-function slotd)))
339     (declare (type function fun))
340     (funcall fun object)))
341
342 (defmethod (setf slot-value-using-class)
343     (new-value
344      (class condition-class)
345      (object condition)
346      (slotd condition-effective-slot-definition))
347   (let ((fun (slot-definition-writer-function slotd)))
348     (declare (type function fun))
349     (funcall fun new-value object)))
350
351 (defmethod slot-boundp-using-class
352     ((class condition-class)
353      (object condition)
354      (slotd condition-effective-slot-definition))
355   (let ((fun (slot-definition-boundp-function slotd)))
356     (declare (type function fun))
357     (funcall fun object)))
358
359 (defmethod slot-makunbound-using-class ((class condition-class) object slot)
360   (error "attempt to unbind slot ~S in condition object ~S."
361          slot object))
362
363 (defmethod slot-value-using-class
364     ((class structure-class)
365      (object structure-object)
366      (slotd structure-effective-slot-definition))
367   (let* ((function (slot-definition-internal-reader-function slotd))
368          (value (funcall function object)))
369     (declare (type function function))
370     ;; FIXME: Is this really necessary? Structure slots should surely
371     ;; never be unbound!
372     (if (eq value +slot-unbound+)
373         (values (slot-unbound class object (slot-definition-name slotd)))
374         value)))
375
376 (defmethod (setf slot-value-using-class)
377     (new-value (class structure-class)
378                (object structure-object)
379                (slotd structure-effective-slot-definition))
380   (let ((function (slot-definition-internal-writer-function slotd)))
381     (declare (type function function))
382     (funcall function new-value object)))
383
384 (defmethod slot-boundp-using-class
385            ((class structure-class)
386             (object structure-object)
387             (slotd structure-effective-slot-definition))
388   t)
389
390 (defmethod slot-makunbound-using-class
391            ((class structure-class)
392             (object structure-object)
393             (slotd structure-effective-slot-definition))
394   (error "Structure slots can't be unbound."))
395 \f
396 (defmethod slot-missing
397            ((class t) instance slot-name operation &optional new-value)
398   (error "~@<When attempting to ~A, the slot ~S is missing from the ~
399           object ~S.~@:>"
400          (ecase operation
401            (slot-value "read the slot's value (slot-value)")
402            (setf (format nil
403                          "set the slot's value to ~S (SETF of SLOT-VALUE)"
404                          new-value))
405            (slot-boundp "test to see whether slot is bound (SLOT-BOUNDP)")
406            (slot-makunbound "make the slot unbound (SLOT-MAKUNBOUND)"))
407          slot-name
408          instance))
409
410 (defmethod slot-unbound ((class t) instance slot-name)
411   (restart-case
412       (error 'unbound-slot :name slot-name :instance instance)
413     (use-value (v)
414       :report "Return a value as the slot-value."
415       :interactive read-evaluated-form
416       v)
417     (store-value (v)
418       :report "Store and return a value as the slot-value."
419       :interactive read-evaluated-form
420       (setf (slot-value instance slot-name) v))))
421
422 (defun slot-unbound-internal (instance position)
423   (values
424    (slot-unbound
425     (class-of instance)
426     instance
427     (etypecase position
428       (fixnum
429        (nth position (wrapper-instance-slots-layout (wrapper-of instance))))
430       (cons
431        (car position))))))
432 \f
433 ;;; FIXME: AMOP says that allocate-instance imples finalize-inheritance
434 ;;; if the class is not yet finalized, but we don't seem to be taking
435 ;;; care of this for non-standard-classes.x
436 (defmethod allocate-instance ((class standard-class) &rest initargs)
437   (declare (ignore initargs))
438   (unless (class-finalized-p class)
439     (finalize-inheritance class))
440   (allocate-standard-instance (class-wrapper class)))
441
442 (defmethod allocate-instance ((class structure-class) &rest initargs)
443   (declare (ignore initargs))
444   (let ((constructor (class-defstruct-constructor class)))
445     (if constructor
446         (funcall constructor)
447         (error "Don't know how to allocate ~S" class))))
448
449 ;;; FIXME: It would be nicer to have allocate-instance return
450 ;;; uninitialized objects for conditions as well.
451 (defmethod allocate-instance ((class condition-class) &rest initargs)
452   (declare (ignore initargs))
453   (make-condition (class-name class)))
454
455 (defmethod allocate-instance ((class built-in-class) &rest initargs)
456   (declare (ignore initargs))
457   (error "Cannot allocate an instance of ~S." class)) ; So sayeth AMOP
458
459 ;;; AMOP says that CLASS-SLOTS signals an error for unfinalized classes.
460 (defmethod class-slots :before ((class slot-class))
461   (unless (class-finalized-p class)
462     (error 'simple-reference-error
463            :format-control "~S called on ~S, which is not yet finalized."
464            :format-arguments (list 'class-slots class)
465            :references (list '(:amop :generic-function class-slots)))))