0.7.13.pcl-class.1
[sbcl.git] / src / pcl / std-class.lisp
index ef6f9cf..910612f 100644 (file)
                    :definition-source `((defclass ,name)
                                         ,*load-pathname*)
                    other)))
-    ;; Defclass of a class with a forward-referenced superclass does not
-    ;; have a wrapper. RES is the incomplete PCL class. The Lisp class
-    ;; does not yet exist. Maybe should return NIL in that case as RES
-    ;; is not useful to the user?
-    (and (class-wrapper res) (sb-kernel:layout-class (class-wrapper res)))))
+    res))
 
 (setf (gdefinition 'load-defclass) #'real-load-defclass)
 
 (defmethod ensure-class-using-class (name (class null) &rest args &key)
   (multiple-value-bind (meta initargs)
       (ensure-class-values class args)
+    (set-class-type-translation (class-prototype meta) name)
     (setf class (apply #'make-instance meta :name name initargs)
          (find-class name) class)
+    (set-class-type-translation class name)
     class))
 
 (defmethod ensure-class-using-class (name (class pcl-class) &rest args &key)
     (unless (eq (class-of class) meta) (change-class class meta))
     (apply #'reinitialize-instance class initargs)
     (setf (find-class name) class)
+    (set-class-type-translation class name)
     class))
 
 (defmethod class-predicate-name ((class t))
     ;; However, after playing around a little, I couldn't find that
     ;; way, so I've left it as is, but if someone does come up with a
     ;; better way... -- CSR, 2002-09-08
-    (loop for (slot . more) on (getf initargs :direct-slots)
-         for slot-name = (getf slot :name)
-         if (some (lambda (s) (eq slot-name (getf s :name))) more) 
-         ;; FIXME: It's quite possible that we ought to define an
-         ;; SB-INT:PROGRAM-ERROR function to signal these and other
-         ;; errors throughout the code base that are required to be
-         ;; of type PROGRAM-ERROR.
-         do (error 'simple-program-error 
-                   :format-control "More than one direct slot with name ~S."
-                   :format-arguments (list slot-name))
-         else 
-         do (loop for (option value . more) on slot by #'cddr
-                  when (and (member option 
-                                    '(:allocation :type 
+    (do ((direct-slots (getf initargs :direct-slots) (cdr direct-slots)))
+       ((endp direct-slots) nil)
+      (destructuring-bind (slot &rest more) direct-slots
+       (let ((slot-name (getf slot :name)))
+         (when (some (lambda (s) (eq slot-name (getf s :name))) more)
+           ;; FIXME: It's quite possible that we ought to define an
+           ;; SB-INT:PROGRAM-ERROR function to signal these and other
+           ;; errors throughout the codebase that are required to be
+           ;; of type PROGRAM-ERROR.
+           (error 'simple-program-error
+                  :format-control "~@<There is more than one direct slot ~
+                                   with name ~S.~:>"
+                  :format-arguments (list slot-name)))
+         (do ((stuff slot (cddr stuff)))
+             ((endp stuff) nil)
+           (destructuring-bind (option value &rest more) stuff
+             (cond
+               ((and (member option '(:allocation :type
                                       :initform :documentation))
-                            (not (eq unsupplied
-                                     (getf more option unsupplied)))) 
-                  do (error 'simple-program-error 
-                            :format-control "Duplicate slot option ~S for slot ~S."
-                            :format-arguments (list option slot-name))))
+                     (not (eq unsupplied
+                              (getf more option unsupplied))))
+                (error 'simple-program-error
+                       :format-control "~@<Duplicate slot option ~S for ~
+                                        slot named ~S.~:>"
+                       :format-arguments (list option slot-name)))
+               ((and (eq option :readers)
+                     (notevery #'symbolp value))
+                (error 'simple-program-error
+                       :format-control "~@<Slot reader names for slot ~
+                                        named ~S must be symbols.~:>"
+                       :format-arguments (list slot-name)))
+               ((and (eq option :initargs)
+                     (notevery #'symbolp value))
+                (error 'simple-program-error
+                       :format-control "~@<Slot initarg names for slot ~
+                                        named ~S must be symbols.~:>"
+                       :format-arguments (list slot-name)))))))))
     (loop for (initarg . more) on (getf initargs :direct-default-initargs)
          for name = (car initarg) 
          when (some (lambda (a) (eq (car a) name)) more) 
          do (error 'simple-program-error 
-                   :format-control "Duplicate initialization argument ~
-                                     name ~S in :default-initargs of class ~A."
+                   :format-control "~@<Duplicate initialization argument ~
+                                    name ~S in :DEFAULT-INITARGS.~:>"
                    :format-arguments (list name class)))
-    (loop (unless (remf initargs :metaclass) (return)))
+    (let ((metaclass 0)
+         (default-initargs 0))
+      (do ((args initargs (cddr args)))
+         ((endp args) nil)
+       (case (car args)
+         (:metaclass
+          (when (> (incf metaclass) 1)
+            (error 'simple-program-error
+                   :format-control "~@<More than one :METACLASS ~
+                                    option specified.~:>")))
+         (:direct-default-initargs
+          (when (> (incf default-initargs) 1)
+            (error 'simple-program-error
+                   :format-control "~@<More than one :DEFAULT-INITARGS ~
+                                    option specified.~:>"))))))
+    (remf initargs :metaclass)
     (loop (unless (remf initargs :direct-superclasses) (return)))
     (loop (unless (remf initargs :direct-slots) (return)))
     (values meta
                  (lambda (dependent)
                    (apply #'update-dependent class dependent initargs))))
 
-(defmethod shared-initialize :after ((slotd structure-slot-definition)
-                                    slot-names
-                                    &key (allocation :instance))
-  (declare (ignore slot-names))
+(defmethod shared-initialize :after
+    ((slotd structure-slot-definition) slot-names &key
+     (allocation :instance) allocation-class)
+  (declare (ignore slot-names allocation-class))
   (unless (eq allocation :instance)
     (error "Structure slots must have :INSTANCE allocation.")))
 
                                     +slot-unbound+))
                                 direct-slots)))
          (reader-names (mapcar (lambda (slotd)
-                                 (intern (format nil
-                                                 "~A~A reader"
-                                                 conc-name
-                                                 (slot-definition-name
-                                                  slotd))))
+                                 (list 'slot-accessor name
+                                      (slot-definition-name slotd)
+                                      'reader))
                                direct-slots))
          (writer-names (mapcar (lambda (slotd)
-                                 (intern (format nil
-                                                 "~A~A writer"
-                                                 conc-name
-                                                 (slot-definition-name
-                                                  slotd))))
+                                 (list 'slot-accessor name
+                                      (slot-definition-name slotd)
+                                      'writer))
                                direct-slots))
          (readers-init
            (mapcar (lambda (slotd reader-name)
     (setf (slot-value class 'class-precedence-list)
             (compute-class-precedence-list class))
     (setf (slot-value class 'slots) (compute-slots class))
-    (let ((lclass (cl:find-class (class-name class))))
-      (setf (sb-kernel:class-pcl-class lclass) class)
-      (setf (slot-value class 'wrapper) (sb-kernel:class-layout lclass)))
+    (let ((lclass (sb-kernel:find-classoid (class-name class))))
+      (setf (sb-kernel:classoid-pcl-class lclass) class)
+      (setf (slot-value class 'wrapper) (sb-kernel:classoid-layout lclass)))
     (update-pv-table-cache-info class)
     (setq predicate-name (if predicate-name-p
                           (setf (slot-value class 'predicate-name)
 \f
 (defmethod finalize-inheritance ((class std-class))
   (update-class class t))
+
+(defmethod finalize-inheritance ((class forward-referenced-class))
+  ;; FIXME: should we not be thinking a bit about what kinds of error
+  ;; we're throwing?  Maybe we need a clos-error type to mix in?  Or
+  ;; possibly a forward-referenced-class-error, though that's
+  ;; difficult given e.g. class precedence list calculations...
+  (error
+   "~@<FINALIZE-INHERITANCE was called on a forward referenced class:~
+       ~2I~_~S~:>"
+   class))
+
 \f
 (defun class-has-a-forward-referenced-superclass-p (class)
   (or (forward-referenced-class-p class)
     (update-slots class (compute-slots class))
     (update-gfs-of-class class)
     (update-inits class (compute-default-initargs class))
-    (update-make-instance-function-table class))
+    (update-ctors 'finalize-inheritance :class class))
   (unless finalizep
     (dolist (sub (class-direct-subclasses class)) (update-class sub nil))))
 
             collect))
     (nreverse collect)))
 
-(defun compute-layout (cpl instance-eslotds)
-  (let* ((names
-          (let (collect)
-            (dolist (eslotd instance-eslotds)
-              (when (eq (slot-definition-allocation eslotd) :instance)
-                (push (slot-definition-name eslotd) collect)))
-             (nreverse collect)))
-        (order ()))
-    (labels ((rwalk (tail)
-              (when tail
-                (rwalk (cdr tail))
-                (dolist (ss (class-slots (car tail)))
-                  (let ((n (slot-definition-name ss)))
-                    (when (member n names)
-                      (setq order (cons n order)
-                            names (remove n names))))))))
-      (rwalk (if (slot-boundp (car cpl) 'slots)
-                cpl
-                (cdr cpl)))
-      (reverse (append names order)))))
-
 (defun update-gfs-of-class (class)
   (when (and (class-finalized-p class)
             (let ((cpl (class-precedence-list class)))
   ;; The list is in most-specific-first order.
   (let ((name-dslotds-alist ()))
     (dolist (c (class-precedence-list class))
-      (let ((dslotds (class-direct-slots c)))
-       (dolist (d dslotds)
-         (let* ((name (slot-definition-name d))
-                (entry (assq name name-dslotds-alist)))
-           (if entry
-               (push d (cdr entry))
-               (push (list name d) name-dslotds-alist))))))
+      (dolist (slot (class-direct-slots c))
+       (let* ((name (slot-definition-name slot))
+              (entry (assq name name-dslotds-alist)))
+         (if entry
+             (push slot (cdr entry))
+             (push (list name slot) name-dslotds-alist)))))
     (mapcar (lambda (direct)
              (compute-effective-slot-definition class
                                                 (nreverse (cdr direct))))
            name-dslotds-alist)))
 
-(defmethod compute-slots :around ((class std-class))
+(defmethod compute-slots ((class standard-class))
+  (call-next-method))
+
+(defmethod compute-slots :around ((class standard-class))
   (let ((eslotds (call-next-method))
-       (cpl (class-precedence-list class))
-       (instance-slots ())
-       (class-slots    ()))
-    (dolist (eslotd eslotds)
-      (let ((alloc (slot-definition-allocation eslotd)))
-       (case alloc
-          (:instance (push eslotd instance-slots))
-          (:class (push eslotd class-slots)))))
-    (let ((nlayout (compute-layout cpl instance-slots)))
-      (dolist (eslotd instance-slots)
-       (setf (slot-definition-location eslotd)
-             (position (slot-definition-name eslotd) nlayout))))
-    (dolist (eslotd class-slots)
+       (location -1))
+    (dolist (eslotd eslotds eslotds)
       (setf (slot-definition-location eslotd)
-           (assoc (slot-definition-name eslotd)
-                  (class-slot-cells (slot-definition-class eslotd)))))
-    (mapc #'initialize-internal-slot-functions eslotds)
-    eslotds))
+           (ecase (slot-definition-allocation eslotd)
+             (:instance
+              (incf location))
+             (:class
+              (let* ((name (slot-definition-name eslotd))
+                     (from-class (slot-definition-allocation-class eslotd))
+                     (cell (assq name (class-slot-cells from-class))))
+                (aver (consp cell))
+                cell))))
+      (initialize-internal-slot-functions eslotd))))
+
+(defmethod compute-slots ((class funcallable-standard-class))
+  (call-next-method))
+
+(defmethod compute-slots :around ((class funcallable-standard-class))
+  (labels ((instance-slot-names (slotds)
+            (let (collect)
+              (dolist (slotd slotds (nreverse collect))
+                (when (eq (slot-definition-allocation slotd) :instance)
+                  (push (slot-definition-name slotd) collect)))))
+          ;; This sorts slots so that slots of classes later in the CPL
+           ;; come before slots of other classes.  This is crucial for
+           ;; funcallable instances because it ensures that the slots of
+           ;; FUNCALLABLE-STANDARD-OBJECT, which includes the slots of
+           ;; KERNEL:FUNCALLABLE-INSTANCE, come first, which in turn
+           ;; makes it possible to treat FUNCALLABLE-STANDARD-OBJECT as
+           ;; a funcallable instance.
+          (compute-layout (eslotds)
+            (let ((first ())
+                  (names (instance-slot-names eslotds)))
+              (dolist (class
+                       (reverse (class-precedence-list class))
+                       (nreverse (nconc names first)))
+                (dolist (ss (class-slots class))
+                  (let ((name (slot-definition-name ss)))
+                    (when (member name names)
+                      (push name first)
+                      (setq names (delete name names)))))))))
+    (let ((all-slotds (call-next-method))
+         (instance-slots ())
+         (class-slots ()))
+      (dolist (slotd all-slotds)
+       (ecase (slot-definition-allocation slotd)
+         (:instance (push slotd instance-slots))
+         (:class (push slotd class-slots))))
+      (let ((layout (compute-layout instance-slots)))
+       (dolist (slotd instance-slots)
+         (setf (slot-definition-location slotd)
+               (position (slot-definition-name slotd) layout))
+         (initialize-internal-slot-functions slotd)))
+      (dolist (slotd class-slots)
+       (let ((name (slot-definition-name slotd))
+             (from-class (slot-definition-allocation-class slotd)))
+         (setf (slot-definition-location slotd)
+               (assoc name (class-slot-cells from-class)))
+         (aver (consp (slot-definition-location slotd)))
+         (initialize-internal-slot-functions slotd)))
+      all-slotds)))
 
 (defmethod compute-slots ((class structure-class))
   (mapcan (lambda (superclass)
         (initform nil)
         (initargs nil)
         (allocation nil)
+        (allocation-class nil)
         (type t)
         (namep  nil)
         (initp  nil)
                  initp t)))
        (unless allocp
          (setq allocation (slot-definition-allocation slotd)
+               allocation-class (slot-definition-class slotd)
                allocp t))
        (setq initargs (append (slot-definition-initargs slotd) initargs))
        (let ((slotd-type (slot-definition-type slotd)))
          :initfunction initfunction
          :initargs initargs
          :allocation allocation
+         :allocation-class allocation-class
          :type type
          :class class)))
 
     (or (eq new-super-meta-class *the-class-std-class*)
        (eq (class-of class) new-super-meta-class))))
 \f
+;;; What this does depends on which of the four possible values of
+;;; LAYOUT-INVALID the PCL wrapper has; the simplest case is when it
+;;; is (:FLUSH <wrapper>) or (:OBSOLETE <wrapper>), when there is
+;;; nothing to do, as the new wrapper has already been created.  If
+;;; LAYOUT-INVALID returns NIL, then we invalidate it (setting it to
+;;; (:FLUSH <wrapper>); UPDATE-SLOTS later gets to choose whether or
+;;; not to "upgrade" this to (:OBSOLETE <wrapper>).
+;;;
+;;; This leaves the case where LAYOUT-INVALID returns T, which happens
+;;; when REGISTER-LAYOUT has invalidated a superclass of CLASS (which
+;;; invalidated all the subclasses in SB-KERNEL land).  Again, here we
+;;; must flush the caches and allow UPDATE-SLOTS to decide whether to
+;;; obsolete the wrapper.
+;;;
+;;; FIXME: either here or in INVALID-WRAPPER-P looks like a good place
+;;; for (AVER (NOT (EQ (SB-KERNEL:LAYOUT-INVALID OWRAPPER)
+;;;                    :UNINITIALIZED)))
+;;;
+;;; Thanks to Gerd Moellmann for the explanation.  -- CSR, 2002-10-29
 (defun force-cache-flushes (class)
   (let* ((owrapper (class-wrapper class)))
     ;; We only need to do something if the wrapper is still valid. If
     ;; particular, we must be sure we never change an OBSOLETE into a
     ;; FLUSH since OBSOLETE means do what FLUSH does and then some.
     (when (or (not (invalid-wrapper-p owrapper))
-             ;; Ick. LAYOUT-INVALID can return a list (which we can
-             ;; handle), T (which we can't), NIL (which is handled by
-             ;; INVALID-WRAPPER-P) or :UNINITIALIZED (which never
-             ;; gets here (I hope).  -- CSR, 2002-10-28
+             ;; KLUDGE: despite the observations above, this remains
+             ;; a violation of locality or what might be considered
+             ;; good style.  There has to be a better way!  -- CSR,
+             ;; 2002-10-29
              (eq (sb-kernel:layout-invalid owrapper) t))
       (let ((nwrapper (make-wrapper (wrapper-no-of-instance-slots owrapper)
                                    class)))
   (or (eq s *the-class-t*)
       (eq s *the-class-stream*)))
 \f
+;;; Some necessary methods for FORWARD-REFERENCED-CLASS
+(defmethod class-direct-slots ((class forward-referenced-class)) ())
+(defmethod class-direct-default-initargs ((class forward-referenced-class)) ())
+(macrolet ((def (method)
+             `(defmethod ,method ((class forward-referenced-class))
+                (error "~@<~I~S was called on a forward referenced class:~2I~_~S~:>"
+                       ',method class))))
+  (def class-default-initargs)
+  (def class-precedence-list)
+  (def class-slots))
+
 (defmethod validate-superclass ((c slot-class)
                                (f forward-referenced-class))
   t)