Fix make-array transforms.
[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              (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))))))))
44
45 (defmethod wrapper-fetcher ((class standard-class))
46   'std-instance-wrapper)
47
48 (defmethod slots-fetcher ((class standard-class))
49   'std-instance-slots)
50
51 (defmethod raw-instance-allocator ((class standard-class))
52   'allocate-standard-instance)
53
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.
60
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)))
76         (t
77          (error "unrecognized instance type"))))
78 \f
79 ;;;; STANDARD-INSTANCE-ACCESS
80
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)))
87
88 (defun standard-instance-access (instance location)
89   (clos-slots-ref (std-instance-slots instance) location))
90
91 (defun (setf standard-instance-access) (new-value instance location)
92   (setf (clos-slots-ref (std-instance-slots instance) location) new-value))
93
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))
97
98 (defun funcallable-standard-instance-access (instance location)
99   (clos-slots-ref (fsc-instance-slots instance) location))
100
101 (defun (setf funcallable-standard-instance-access) (new-value instance location)
102   (setf (clos-slots-ref (fsc-instance-slots instance) location) new-value))
103
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))
107 \f
108 ;;;; SLOT-VALUE, (SETF SLOT-VALUE), SLOT-BOUNDP, SLOT-MAKUNBOUND
109
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)))
128                 (t
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)))
133
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))
142
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))
163           (t
164            (bug "Bogus slot-cell in SET-SLOT-VALUE: ~S" cell))))
165   new-value)
166
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))
173
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))
188
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))
210                      (t
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))))
216
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)))
235                 (t
236                  (bug "Bogus slot cell in SLOT-VALUE: ~S" cell)))))
237     (not (eq +slot-unbound+ value))))
238
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))
245
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))))
262           (t
263            (bug "Bogus slot-cell in SLOT-MAKUNBOUND: ~S" cell))))
264   object)
265
266 (defun slot-exists-p (object slot-name)
267   (let ((class (class-of object)))
268     (not (null (find-slot-definition class slot-name)))))
269
270 (defvar *unbound-slot-value-marker* (make-unprintable-object "unbound slot"))
271
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))
279
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))
300             (t
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)))
306
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))
332       (t
333        (instance-structure-protocol-error
334         slotd '(setf slot-value-using-class))))))
335
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))
357             (t
358              (instance-structure-protocol-error slotd
359                                                 'slot-boundp-using-class)))))
360     (not (eq value +slot-unbound+))))
361
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+))
380       (t
381        (instance-structure-protocol-error slotd
382                                           'slot-makunbound-using-class))))
383   object)
384
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)))
391
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)))
399
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)))
406
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))
410
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)))
423
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)))
431
432 (defmethod slot-boundp-using-class
433            ((class structure-class)
434             (object structure-object)
435             (slotd structure-effective-slot-definition))
436   t)
437
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."))
443 \f
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))
457
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
464       v)
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))))
469
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 (wrapper-slots (wrapper-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))))))
488 \f
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)))
497
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))))
504
505 (defmethod allocate-instance ((class condition-class) &rest initargs)
506   (declare (ignore initargs))
507   (allocate-condition (class-name class)))
508
509 (defmethod allocate-instance ((class built-in-class) &rest initargs)
510   (declare (ignore initargs))
511   (error "Cannot allocate an instance of ~S." class)) ; So sayeth AMOP
512
513 ;;; AMOP says that CLASS-SLOTS signals an error for unfinalized classes.
514 (defmethod class-slots :before ((class slot-class))
515   (unless (class-finalized-p class)
516     (error 'simple-reference-error
517            :format-control "~S called on ~S, which is not yet finalized."
518            :format-arguments (list 'class-slots class)
519            :references (list '(:amop :generic-function class-slots)))))