1.0.7.36: FIND-SLOT-DEFINITION to return NIL when called with non-slot-classes
[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 four 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 set-wrapper (inst new)
52   (cond ((std-instance-p inst)
53          (setf (std-instance-wrapper inst) new))
54         ((fsc-instance-p inst)
55          (setf (fsc-instance-wrapper inst) new))
56         (t
57          (error "unrecognized instance type"))))
58
59 (defun swap-wrappers-and-slots (i1 i2)
60   (with-pcl-lock                        ;FIXME is this sufficient?
61    (cond ((std-instance-p i1)
62           (let ((w1 (std-instance-wrapper i1))
63                 (s1 (std-instance-slots i1)))
64             (setf (std-instance-wrapper i1) (std-instance-wrapper i2))
65             (setf (std-instance-slots i1) (std-instance-slots i2))
66             (setf (std-instance-wrapper i2) w1)
67             (setf (std-instance-slots i2) s1)))
68          ((fsc-instance-p i1)
69           (let ((w1 (fsc-instance-wrapper i1))
70                 (s1 (fsc-instance-slots i1)))
71             (setf (fsc-instance-wrapper i1) (fsc-instance-wrapper i2))
72             (setf (fsc-instance-slots i1) (fsc-instance-slots i2))
73             (setf (fsc-instance-wrapper i2) w1)
74             (setf (fsc-instance-slots i2) s1)))
75          (t
76           (error "unrecognized instance type")))))
77 \f
78 ;;;; SLOT-VALUE, (SETF SLOT-VALUE), SLOT-BOUNDP
79
80 (declaim (ftype (sfunction (t symbol) t) slot-value))
81 (defun slot-value (object slot-name)
82   (let* ((class (class-of object))
83          (slot-definition (find-slot-definition class slot-name)))
84     (if (null slot-definition)
85         (values (slot-missing class object slot-name 'slot-value))
86         (slot-value-using-class class object slot-definition))))
87
88 (define-compiler-macro slot-value (&whole form object slot-name
89                                    &environment env)
90   (if (and (constantp slot-name env)
91            (interned-symbol-p (constant-form-value slot-name env)))
92       `(accessor-slot-value ,object ,slot-name)
93       form))
94
95 (defun set-slot-value (object slot-name new-value)
96   (let* ((class (class-of object))
97          (slot-definition (find-slot-definition class slot-name)))
98     (if (null slot-definition)
99         (progn (slot-missing class object slot-name 'setf new-value)
100                new-value)
101         (setf (slot-value-using-class class object slot-definition)
102               new-value))))
103
104 ;;; A version of SET-SLOT-VALUE for use in safe code, where we want to
105 ;;; check types when writing to slots:
106 ;;;   * Doesn't have an optimizing compiler-macro
107 ;;;   * Isn't special-cased in WALK-METHOD-LAMBDA
108 (defun safe-set-slot-value (object slot-name new-value)
109   (set-slot-value object slot-name new-value))
110
111 (define-compiler-macro set-slot-value (&whole form object slot-name new-value
112                                       &environment env)
113   (if (and (constantp slot-name env)
114            (interned-symbol-p (constant-form-value slot-name env))
115            ;; We can't use the ACCESSOR-SET-SLOT-VALUE path in safe
116            ;; code, since it'll use the global automatically generated
117            ;; accessor, which won't do typechecking. (SLOT-OBJECT
118            ;; won't have been compiled with SAFETY 3, so SAFE-P will
119            ;; be NIL in MAKE-STD-WRITER-METHOD-FUNCTION).
120            (not (safe-code-p env)))
121       `(accessor-set-slot-value ,object ,slot-name ,new-value)
122       form))
123
124 (defun slot-boundp (object slot-name)
125   (let* ((class (class-of object))
126          (slot-definition (find-slot-definition class slot-name)))
127     (if (null slot-definition)
128         (not (not (slot-missing class object slot-name 'slot-boundp)))
129         (slot-boundp-using-class class object slot-definition))))
130
131 (setf (gdefinition 'slot-boundp-normal) #'slot-boundp)
132
133 (define-compiler-macro slot-boundp (&whole form object slot-name
134                                     &environment env)
135   (if (and (constantp slot-name env)
136            (interned-symbol-p (constant-form-value slot-name env)))
137       `(accessor-slot-boundp ,object ,slot-name)
138       form))
139
140 (defun slot-makunbound (object slot-name)
141   (let* ((class (class-of object))
142          (slot-definition (find-slot-definition class slot-name)))
143     (if (null slot-definition)
144         (slot-missing class object slot-name 'slot-makunbound)
145         (slot-makunbound-using-class class object slot-definition))
146     object))
147
148 (defun slot-exists-p (object slot-name)
149   (let ((class (class-of object)))
150     (not (null (find-slot-definition class slot-name)))))
151
152 (defvar *unbound-slot-value-marker* (make-unprintable-object "unbound slot"))
153
154 ;;; This isn't documented, but is used within PCL in a number of print
155 ;;; object methods. (See NAMED-OBJECT-PRINT-FUNCTION.)
156 (defun slot-value-or-default (object slot-name &optional
157                               (default *unbound-slot-value-marker*))
158   (if (slot-boundp object slot-name)
159       (slot-value object slot-name)
160       default))
161 \f
162 (defun standard-instance-access (instance location)
163   (clos-slots-ref (std-instance-slots instance) location))
164
165 (defun funcallable-standard-instance-access (instance location)
166   (clos-slots-ref (fsc-instance-slots instance) location))
167
168 (defmethod slot-value-using-class ((class std-class)
169                                    (object standard-object)
170                                    (slotd standard-effective-slot-definition))
171   (check-obsolete-instance object)
172   (let* ((location (slot-definition-location slotd))
173          (value
174           (typecase location
175             (fixnum
176              (cond ((std-instance-p object)
177                     (clos-slots-ref (std-instance-slots object)
178                                     location))
179                    ((fsc-instance-p object)
180                     (clos-slots-ref (fsc-instance-slots object)
181                                     location))
182                    (t (bug "unrecognized instance type in ~S"
183                            'slot-value-using-class))))
184             (cons
185              (cdr location))
186             (t
187              (instance-structure-protocol-error slotd
188                                                 'slot-value-using-class)))))
189     (if (eq value +slot-unbound+)
190         (values (slot-unbound class object (slot-definition-name slotd)))
191         value)))
192
193 (defmethod (setf slot-value-using-class)
194            (new-value (class std-class)
195                       (object standard-object)
196                       (slotd standard-effective-slot-definition))
197   (check-obsolete-instance object)
198   (let ((location (slot-definition-location slotd))
199         (type-check-function
200          (when (safe-p class)
201            (slot-definition-type-check-function slotd))))
202     (flet ((check (new-value)
203              (when type-check-function
204                (funcall (the function type-check-function) new-value))
205              new-value))
206       (typecase location
207         (fixnum
208          (cond ((std-instance-p object)
209                 (setf (clos-slots-ref (std-instance-slots object) location)
210                       (check new-value)))
211                ((fsc-instance-p object)
212                 (setf (clos-slots-ref (fsc-instance-slots object) location)
213                       (check new-value)))
214                 (t (bug "unrecognized instance type in ~S"
215                         '(setf slot-value-using-class)))))
216         (cons
217          (setf (cdr location) (check new-value)))
218         (t
219          (instance-structure-protocol-error
220           slotd '(setf slot-value-using-class)))))))
221
222 (defmethod slot-boundp-using-class
223            ((class std-class)
224             (object standard-object)
225             (slotd standard-effective-slot-definition))
226   (check-obsolete-instance object)
227   (let* ((location (slot-definition-location slotd))
228          (value
229           (typecase location
230             (fixnum
231              (cond ((std-instance-p object)
232                           (clos-slots-ref (std-instance-slots object)
233                                           location))
234                    ((fsc-instance-p object)
235                     (clos-slots-ref (fsc-instance-slots object)
236                                     location))
237                    (t (bug "unrecognized instance type in ~S"
238                            'slot-boundp-using-class))))
239             (cons
240              (cdr location))
241             (t
242              (instance-structure-protocol-error slotd
243                                                 'slot-boundp-using-class)))))
244     (not (eq value +slot-unbound+))))
245
246 (defmethod slot-makunbound-using-class
247            ((class std-class)
248             (object standard-object)
249             (slotd standard-effective-slot-definition))
250   (check-obsolete-instance object)
251   (let ((location (slot-definition-location slotd)))
252     (typecase location
253       (fixnum
254        (cond ((std-instance-p object)
255               (setf (clos-slots-ref (std-instance-slots object) location)
256                     +slot-unbound+))
257              ((fsc-instance-p object)
258               (setf (clos-slots-ref (fsc-instance-slots object) location)
259                     +slot-unbound+))
260              (t (bug "unrecognized instance type in ~S"
261                      'slot-makunbound-using-class))))
262       (cons
263        (setf (cdr location) +slot-unbound+))
264       (t
265        (instance-structure-protocol-error slotd
266                                           'slot-makunbound-using-class))))
267   object)
268
269 (defmethod slot-value-using-class
270     ((class condition-class)
271      (object condition)
272      (slotd condition-effective-slot-definition))
273   (let ((fun (slot-definition-reader-function slotd)))
274     (declare (type function fun))
275     (funcall fun object)))
276
277 (defmethod (setf slot-value-using-class)
278     (new-value
279      (class condition-class)
280      (object condition)
281      (slotd condition-effective-slot-definition))
282   (let ((fun (slot-definition-writer-function slotd)))
283     (declare (type function fun))
284     (funcall fun new-value object)))
285
286 (defmethod slot-boundp-using-class
287     ((class condition-class)
288      (object condition)
289      (slotd condition-effective-slot-definition))
290   (let ((fun (slot-definition-boundp-function slotd)))
291     (declare (type function fun))
292     (funcall fun object)))
293
294 (defmethod slot-makunbound-using-class ((class condition-class) object slot)
295   (error "attempt to unbind slot ~S in condition object ~S."
296          slot object))
297
298 (defmethod slot-value-using-class
299     ((class structure-class)
300      (object structure-object)
301      (slotd structure-effective-slot-definition))
302   (let* ((function (slot-definition-internal-reader-function slotd))
303          (value (funcall function object)))
304     (declare (type function function))
305     (if (eq value +slot-unbound+)
306         (values (slot-unbound class object (slot-definition-name slotd)))
307         value)))
308
309 (defmethod (setf slot-value-using-class)
310     (new-value (class structure-class)
311                (object structure-object)
312                (slotd structure-effective-slot-definition))
313   (let ((function (slot-definition-internal-writer-function slotd)))
314     (declare (type function function))
315     (funcall function new-value object)))
316
317 (defmethod slot-boundp-using-class
318            ((class structure-class)
319             (object structure-object)
320             (slotd structure-effective-slot-definition))
321   t)
322
323 (defmethod slot-makunbound-using-class
324            ((class structure-class)
325             (object structure-object)
326             (slotd structure-effective-slot-definition))
327   (error "Structure slots can't be unbound."))
328 \f
329 (defmethod slot-missing
330            ((class t) instance slot-name operation &optional new-value)
331   (error "~@<When attempting to ~A, the slot ~S is missing from the ~
332           object ~S.~@:>"
333          (ecase operation
334            (slot-value "read the slot's value (slot-value)")
335            (setf (format nil
336                          "set the slot's value to ~S (SETF of SLOT-VALUE)"
337                          new-value))
338            (slot-boundp "test to see whether slot is bound (SLOT-BOUNDP)")
339            (slot-makunbound "make the slot unbound (SLOT-MAKUNBOUND)"))
340          slot-name
341          instance))
342
343 (defmethod slot-unbound ((class t) instance slot-name)
344   (restart-case
345       (error 'unbound-slot :name slot-name :instance instance)
346     (use-value (v)
347       :report "Return a value as the slot-value."
348       :interactive read-evaluated-form
349       v)
350     (store-value (v)
351       :report "Store and return a value as the slot-value."
352       :interactive read-evaluated-form
353       (setf (slot-value instance slot-name) v))))
354
355 (defun slot-unbound-internal (instance position)
356   (values
357    (slot-unbound
358     (class-of instance)
359     instance
360     (etypecase position
361       (fixnum
362        (nth position (wrapper-instance-slots-layout (wrapper-of instance))))
363       (cons
364        (car position))))))
365 \f
366 ;;; FIXME: AMOP says that allocate-instance imples finalize-inheritance
367 ;;; if the class is not yet finalized, but we don't seem to be taking
368 ;;; care of this for non-standard-classes.x
369 (defmethod allocate-instance ((class standard-class) &rest initargs)
370   (declare (ignore initargs))
371   (unless (class-finalized-p class)
372     (finalize-inheritance class))
373   (allocate-standard-instance (class-wrapper class)))
374
375 (defmethod allocate-instance ((class structure-class) &rest initargs)
376   (declare (ignore initargs))
377   (let ((constructor (class-defstruct-constructor class)))
378     (if constructor
379         (funcall constructor)
380         (allocate-standard-instance (class-wrapper class)))))
381
382 ;;; FIXME: It would be nicer to have allocate-instance return
383 ;;; uninitialized objects for conditions as well.
384 (defmethod allocate-instance ((class condition-class) &rest initargs)
385   (declare (ignore initargs))
386   (make-condition (class-name class)))
387
388 (defmethod allocate-instance ((class built-in-class) &rest initargs)
389   (declare (ignore initargs))
390   (error "Cannot allocate an instance of ~S." class)) ; So sayeth AMOP
391