0.7.10.15:
[sbcl.git] / src / code / defstruct.lisp
index 410759c..81af528 100644 (file)
          (t res))))
 
 ;;; Delay looking for compiler-layout until the constructor is being
-;;; compiled, since it doesn't exist until after the EVAL-WHEN (COMPILE)
-;;; stuff is compiled.
+;;; compiled, since it doesn't exist until after the EVAL-WHEN
+;;; (COMPILE) stuff is compiled. (Or, in the oddball case when
+;;; DEFSTRUCT is executing in a non-toplevel context, the
+;;; compiler-layout still doesn't exist at compilation time, and we
+;;; delay still further.)
 (sb!xc:defmacro %delayed-get-compiler-layout (name)
-  `',(compiler-layout-or-lose name))
+  (let ((layout (info :type :compiler-layout name)))
+    (cond (layout
+          ;; ordinary case: When the DEFSTRUCT is at top level,
+          ;; then EVAL-WHEN (COMPILE) stuff will have set up the
+          ;; layout for us to use.
+          (unless (typep (layout-info layout) 'defstruct-description)
+            (error "Class is not a structure class: ~S" name))
+          `,layout)
+         (t
+          ;; KLUDGE: In the case that DEFSTRUCT is not at top-level
+          ;; the layout doesn't exist at compile time. In that case
+          ;; we laboriously look it up at run time. This code will
+          ;; run on every constructor call and will likely be quite
+          ;; slow, so if anyone cares about performance of
+          ;; non-toplevel DEFSTRUCTs, it should be rewritten to be
+          ;; cleverer. -- WHN 2002-10-23
+          (sb!c::compiler-note
+           "implementation limitation: ~
+             Non-toplevel DEFSTRUCT constructors are slow.")
+          (let ((layout (gensym "LAYOUT")))
+            `(let ((,layout (info :type :compiler-layout ',name)))
+               (unless (typep (layout-info ,layout) 'defstruct-description)
+                 (error "Class is not a structure class: ~S" ',name))
+               ,layout))))))
 
 ;;; Get layout right away.
 (sb!xc:defmacro compile-time-find-layout (name)
   ;; a list of DEFSTRUCT-SLOT-DESCRIPTION objects for all slots
   ;; (including included ones)
   (slots () :type list)
+  ;; a list of (NAME . INDEX) pairs for accessors of included structures
+  (inherited-accessor-alist () :type list)
   ;; number of elements we've allocated (See also RAW-LENGTH.)
   (length 0 :type index)
   ;; General kind of implementation.
        (if (dd-class-p dd)
           (let ((inherits (inherits-for-structure dd)))
             `(progn
+               ;; Note we intentionally call %DEFSTRUCT first, and
+               ;; especially before %COMPILER-DEFSTRUCT. %DEFSTRUCT
+               ;; has the tests (and resulting CERROR) for collisions
+               ;; with LAYOUTs which already exist in the runtime. If
+               ;; there are any collisions, we want the user's
+               ;; response to CERROR to control what happens.
+               ;; Especially, if the user responds to the collision
+               ;; with ABORT, we don't want %COMPILER-DEFSTRUCT to
+               ;; modify the definition of the class.
+               (%defstruct ',dd ',inherits)
                (eval-when (:compile-toplevel :load-toplevel :execute)
                  (%compiler-defstruct ',dd ',inherits))
-               (%defstruct ',dd ',inherits)
                ,@(unless expanding-into-code-for-xc-host-p
                    (append ;; FIXME: We've inherited from CMU CL nonparallel
                            ;; code for creating copiers for typed and untyped
                            ;; structures. This should be fixed.
-                                       ;(copier-definition dd)
+                           ;(copier-definition dd)
                            (constructor-definitions dd)
                            (class-method-definitions dd)))
                ',name))
 \f
 ;;;; functions to generate code for various parts of DEFSTRUCT definitions
 
+;;; First, a helper to determine whether a name names an inherited
+;;; accessor.
+(defun accessor-inherited-data (name defstruct)
+  (assoc name (dd-inherited-accessor-alist defstruct) :test #'eq))
+
 ;;; Return a list of forms which create a predicate function for a
 ;;; typed DEFSTRUCT.
 (defun typed-predicate-definitions (defstruct)
              (index (dsd-index slot))
              (slot-type `(and ,(dsd-type slot)
                               ,(dd-element-type defstruct))))
-         (stuff `(proclaim '(inline ,name (setf ,name))))
-         ;; FIXME: The arguments in the next two DEFUNs should be
-         ;; gensyms. (Otherwise e.g. if NEW-VALUE happened to be the
-         ;; name of a special variable, things could get weird.)
-         (stuff `(defun ,name (structure)
-                   (declare (type ,ltype structure))
-                   (the ,slot-type (elt structure ,index))))
-         (unless (dsd-read-only slot)
-           (stuff
-            `(defun (setf ,name) (new-value structure)
-               (declare (type ,ltype structure) (type ,slot-type new-value))
-               (setf (elt structure ,index) new-value)))))))
+         (let ((inherited (accessor-inherited-data name defstruct)))
+           (cond
+             ((not inherited)
+              (stuff `(proclaim '(inline ,name (setf ,name))))
+              ;; FIXME: The arguments in the next two DEFUNs should
+              ;; be gensyms. (Otherwise e.g. if NEW-VALUE happened to
+              ;; be the name of a special variable, things could get
+              ;; weird.)
+              (stuff `(defun ,name (structure)
+                       (declare (type ,ltype structure))
+                       (the ,slot-type (elt structure ,index))))
+              (unless (dsd-read-only slot)
+                (stuff
+                 `(defun (setf ,name) (new-value structure)
+                   (declare (type ,ltype structure) (type ,slot-type new-value))
+                   (setf (elt structure ,index) new-value)))))
+             ((not (= (cdr inherited) index))
+              (style-warn "~@<Non-overwritten accessor ~S does not access ~
+                            slot with name ~S (accessing an inherited slot ~
+                            instead).~:@>" name (dsd-%name slot))))))))
     (stuff)))
 \f
 ;;;; parsing
        (name (dd-name dd)))
     (case (first option)
       (:conc-name
-       (destructuring-bind (conc-name) args
+       (destructuring-bind (&optional conc-name) args
         (setf (dd-conc-name dd)
               (if (symbolp conc-name)
                   conc-name
           accessor, but you can't rely on this behavior, so it'd be wise to ~
           remove the ambiguity in your code.~@:>"
         accessor-name)
-       (setf (dd-predicate-name defstruct) nil)))
+       (setf (dd-predicate-name defstruct) nil))
+      #-sb-xc-host
+      (when (and (fboundp accessor-name)
+                (not (accessor-inherited-data accessor-name defstruct)))
+       (style-warn "redefining ~S in DEFSTRUCT" accessor-name)))
 
     (when default-p
       (setf (dsd-default slot) default))
        (setf (dd-raw-index dd) (dd-raw-index included-structure))
        (setf (dd-raw-length dd) (dd-raw-length included-structure)))
 
+      (setf (dd-inherited-accessor-alist dd)
+           (dd-inherited-accessor-alist included-structure))
       (dolist (included-slot (dd-slots included-structure))
        (let* ((included-name (dsd-name included-slot))
               (modified (or (find included-name modified-slots
                                   :key (lambda (x) (if (atom x) x (car x)))
                                   :test #'string=)
                             `(,included-name))))
+         ;; We stash away an alist of accessors to parents' slots
+         ;; that have already been created to avoid conflicts later
+         ;; so that structures with :INCLUDE and :CONC-NAME (and
+         ;; other edge cases) can work as specified.
+         (when (dsd-accessor-name included-slot)
+           ;; the "oldest" (i.e. highest up the tree of inheritance)
+           ;; will prevail, so don't push new ones on if they
+           ;; conflict.
+           (pushnew (cons (dsd-accessor-name included-slot)
+                          (dsd-index included-slot))
+                    (dd-inherited-accessor-alist dd)
+                    :test #'eq :key #'car))
          (parse-1-dsd dd
                       modified
                       (copy-structure included-slot)))))))
       (let* ((accessor-name (dsd-accessor-name dsd))
             (dsd-type (dsd-type dsd)))
        (when accessor-name
-         (multiple-value-bind (reader-designator writer-designator)
-             (slot-accessor-inline-expansion-designators dd dsd)
-           (sb!xc:proclaim `(ftype (function (,dtype) ,dsd-type)
-                                   ,accessor-name))
-           (setf (info :function :inline-expansion-designator accessor-name)
-                 reader-designator
-                 (info :function :inlinep accessor-name)
-                 :inline)
-           (unless (dsd-read-only dsd)
-             (let ((setf-accessor-name `(setf ,accessor-name)))
-               (sb!xc:proclaim
-                `(ftype (function (,dsd-type ,dtype) ,dsd-type)
-                        ,setf-accessor-name))
-               (setf (info :function
-                           :inline-expansion-designator
-                           setf-accessor-name)
-                     writer-designator
-                     (info :function :inlinep setf-accessor-name)
-                     :inline))))))))
-
+         (let ((inherited (accessor-inherited-data accessor-name dd)))
+           (cond
+             ((not inherited)
+              (multiple-value-bind (reader-designator writer-designator)
+                  (slot-accessor-inline-expansion-designators dd dsd)
+                (sb!xc:proclaim `(ftype (function (,dtype) ,dsd-type)
+                                  ,accessor-name))
+                (setf (info :function :inline-expansion-designator
+                            accessor-name)
+                      reader-designator
+                      (info :function :inlinep accessor-name)
+                      :inline)
+                (unless (dsd-read-only dsd)
+                  (let ((setf-accessor-name `(setf ,accessor-name)))
+                    (sb!xc:proclaim
+                     `(ftype (function (,dsd-type ,dtype) ,dsd-type)
+                       ,setf-accessor-name))
+                    (setf (info :function
+                                :inline-expansion-designator
+                                setf-accessor-name)
+                          writer-designator
+                          (info :function :inlinep setf-accessor-name)
+                          :inline)))))
+             ((not (= (cdr inherited) (dsd-index dsd)))
+              (style-warn "~@<Non-overwritten accessor ~S does not access ~
+                            slot with name ~S (accessing an inherited slot ~
+                            instead).~:@>"
+                          accessor-name
+                          (dsd-%name dsd)))))))))
   (values))
 \f
 ;;;; redefinition stuff
   (declare (type sb!xc:class class) (type layout old-layout new-layout))
   (let ((name (class-proper-name class)))
     (restart-case
-       (error "redefining class ~S incompatibly with the current definition"
+       (error "~@<attempt to redefine the ~S class ~S incompatibly with the current definition~:@>"
+              'structure-object
               name)
       (continue ()
-       :report "Invalidate current definition."
-       (warn "Previously loaded ~S accessors will no longer work." name)
-       (register-layout new-layout))
+       :report (lambda (s)
+                (format s
+                        "~@<Use the new definition of ~S, invalidating ~
+                          already-loaded code and instances.~@:>"
+                        name))
+       (register-layout new-layout))
+      (recklessly-continue ()
+       :report (lambda (s)
+                (format s
+                        "~@<Use the new definition of ~S as if it were ~
+                          compatible, allowing old accessors to use new ~
+                          instances and allowing new accessors to use old ~
+                          instances.~@:>"
+                        name))
+       ;; classic CMU CL warning: "Any old ~S instances will be in a bad way. 
+       ;; I hope you know what you're doing..."
+       (register-layout new-layout
+                       :invalidate nil
+                       :destruct-layout old-layout))
       (clobber-it ()
-       :report "Smash current layout, preserving old code."
-       (warn "Any old ~S instances will be in a bad way.~@
-              I hope you know what you're doing..."
-             name)
-       (register-layout new-layout :invalidate nil
-                        :destruct-layout old-layout))))
+       ;; FIXME: deprecated 2002-10-16, and since it's only interactive
+       ;; hackery instead of a supported feature, can probably be deleted
+       ;; in early 2003
+       :report "(deprecated synonym for RECKLESSLY-CONTINUE)"
+       (register-layout new-layout
+                       :invalidate nil
+                       :destruct-layout old-layout))))
   (values))
 
 ;;; This is called when we are about to define a structure class. It
        (undefine-fun-name (dd-predicate-name info))
        (dolist (slot (dd-slots info))
          (let ((fun (dsd-accessor-name slot)))
-           (undefine-fun-name fun)
-           (unless (dsd-read-only slot)
-             (undefine-fun-name `(setf ,fun))))))
+           (unless (accessor-inherited-data fun info)
+             (undefine-fun-name fun)
+             (unless (dsd-read-only slot)
+               (undefine-fun-name `(setf ,fun)))))))
       ;; Clear out the SPECIFIER-TYPE cache so that subsequent
       ;; references are unknown types.
       (values-specifier-type-cache-clear)))
        (let ((,instance (truly-the ,(dd-name dd)
                          (%make-instance-with-layout
                           (%delayed-get-compiler-layout ,(dd-name dd))))))
-        (declare (optimize (safety 0))) ; Suppress redundant slot type checks.
         ,@(when raw-index
             `((setf (%instance-ref ,instance ,raw-index)
                     (make-array ,(dd-raw-length dd)
 ;;; Given a structure and a BOA constructor spec, call CREATOR with
 ;;; the appropriate args to make a constructor.
 (defun create-boa-constructor (defstruct boa creator)
-  (multiple-value-bind (req opt restp rest keyp keys allowp aux)
-      (sb!kernel:parse-lambda-list (second boa))
+  (multiple-value-bind (req opt restp rest keyp keys allowp auxp aux)
+      (parse-lambda-list (second boa))
     (collect ((arglist)
              (vars)
              (types))
 
        (when allowp (arglist '&allow-other-keys))
 
-       (when aux
+       (when auxp
          (arglist '&aux)
          (dolist (arg aux)
            (let* ((arg (if (consp arg) arg (list arg)))
                         (let ((dsd (find (symbol-name slot-name) dd-slots
                                          :key #'dsd-%name
                                          :test #'string=)))
+                          ;; KLUDGE: bug 117 bogowarning.  Neither
+                          ;; DECLAREing the type nor TRULY-THE cut
+                          ;; the mustard -- it still gives warnings.
+                          (enforce-type dsd defstruct-slot-description)
                           `(setf (,(dsd-accessor-name dsd) ,object-gensym)
-                                 ,slot-name)))
+                                 ,slot-name)))
                       slot-names)
             ,object-gensym))