X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fdefstruct.lisp;h=b0f374b0bf7629b017ac196f45b7c792ecafb2cb;hb=67dc5cf478dfe5e3f517001febb9a8f7b922eacf;hp=c0205452502c2764ef439f0829a80a15cd4466e4;hpb=670010e3f3dcd62efaf23f61abdc73950edb88c6;p=sbcl.git diff --git a/src/code/defstruct.lisp b/src/code/defstruct.lisp index c020545..b0f374b 100644 --- a/src/code/defstruct.lisp +++ b/src/code/defstruct.lisp @@ -49,7 +49,7 @@ ;; 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 + (sb!c:compiler-notify "implementation limitation: ~ Non-toplevel DEFSTRUCT constructors are slow.") (with-unique-names (layout) @@ -174,8 +174,8 @@ (:conc-name dsd-) (:copier nil) #-sb-xc-host (:pure t)) - ;; string name of slot - %name + ;; name of slot + name ;; its position in the implementation sequence (index (missing-arg) :type fixnum) ;; the name of the accessor function @@ -204,14 +204,6 @@ (def!method print-object ((x defstruct-slot-description) stream) (print-unreadable-object (x stream :type t) (prin1 (dsd-name x) stream))) - -;;; Return the name of a defstruct slot as a symbol. We store it as a -;;; string to avoid creating lots of worthless symbols at load time. -(defun dsd-name (dsd) - (intern (string (dsd-%name dsd)) - (if (dsd-accessor-name dsd) - (symbol-package (dsd-accessor-name dsd)) - (sane-package)))) ;;;; typed (non-class) structures @@ -223,7 +215,7 @@ ;;;; shared machinery for inline and out-of-line slot accessor functions -(eval-when (:compile-toplevel :load-toplevel :execute) +(eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute) ;; information about how a slot of a given DSD-RAW-TYPE is to be accessed (defstruct raw-slot-data @@ -345,15 +337,18 @@ (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. + ;; Note we intentionally enforce package locks and + ;; 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. + (with-single-package-locked-error + (:symbol ',name "defining ~A as a structure")) (%defstruct ',dd ',inherits) (eval-when (:compile-toplevel :load-toplevel :execute) (%compiler-defstruct ',dd ',inherits)) @@ -366,6 +361,8 @@ (class-method-definitions dd))) ',name)) `(progn + (with-single-package-locked-error + (:symbol ',name "defining ~A as a structure")) (eval-when (:compile-toplevel :load-toplevel :execute) (setf (info :typed-structure :info ',name) ',dd)) ,@(unless expanding-into-code-for-xc-host-p @@ -432,15 +429,16 @@ (and (typep ,argname ',ltype) ,(cond ((subtypep ltype 'list) - `(consp (nthcdr ,name-index (the ,ltype ,argname)))) + `(do ((head (the ,ltype ,argname) (cdr head)) + (i 0 (1+ i))) + ((or (not (consp head)) (= i ,name-index)) + (and (consp head) (eq ',name (car head)))))) ((subtypep ltype 'vector) - `(= (length (the ,ltype ,argname)) - ,(dd-length defstruct))) + `(and (= (length (the ,ltype ,argname)) + ,(dd-length defstruct)) + (eq ',name (aref (the ,ltype ,argname) ,name-index)))) (t (bug "Uncatered-for lisp type in typed DEFSTRUCT: ~S." - ltype))) - (eq (elt (the ,ltype ,argname) - ,name-index) - ',name)))))))) + ltype)))))))))) ;;; Return a list of forms to create a copier function of a typed DEFSTRUCT. (defun typed-copier-definitions (defstruct) @@ -463,7 +461,7 @@ (let ((inherited (accessor-inherited-data name defstruct))) (cond ((not inherited) - (stuff `(proclaim '(inline ,name (setf ,name)))) + (stuff `(declaim (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 @@ -479,7 +477,7 @@ ((not (= (cdr inherited) index)) (style-warn "~@" name (dsd-%name slot)))))))) + instead).~:@>" name (dsd-name slot)))))))) (stuff))) ;;;; parsing @@ -608,33 +606,39 @@ ;;; that we modify to get the new slot. This is supplied when handling ;;; included slots. (defun parse-1-dsd (defstruct spec &optional - (slot (make-defstruct-slot-description :%name "" + (slot (make-defstruct-slot-description :name "" :index 0 :type t))) (multiple-value-bind (name default default-p type type-p read-only ro-p) - (cond - ((listp spec) - (destructuring-bind - (name - &optional (default nil default-p) - &key (type nil type-p) (read-only nil ro-p)) - spec - (values name - default default-p - (uncross type) type-p - read-only ro-p))) - (t - (when (keywordp spec) - (style-warn "Keyword slot name indicates probable syntax ~ - error in DEFSTRUCT: ~S." - spec)) - spec)) - - (when (find name (dd-slots defstruct) :test #'string= :key #'dsd-%name) + (typecase spec + (symbol + (when (keywordp spec) + (style-warn "Keyword slot name indicates probable syntax ~ + error in DEFSTRUCT: ~S." + spec)) + spec) + (cons + (destructuring-bind + (name + &optional (default nil default-p) + &key (type nil type-p) (read-only nil ro-p)) + spec + (values name + default default-p + (uncross type) type-p + read-only ro-p))) + (t (error 'simple-program-error + :format-control "in DEFSTRUCT, ~S is not a legal slot ~ + description." + :format-arguments (list spec)))) + + (when (find name (dd-slots defstruct) + :test #'string= + :key (lambda (x) (symbol-name (dsd-name x)))) (error 'simple-program-error :format-control "duplicate slot name ~S" :format-arguments (list name))) - (setf (dsd-%name slot) (string name)) + (setf (dsd-name slot) name) (setf (dd-slots defstruct) (nconc (dd-slots defstruct) (list slot))) (let ((accessor-name (if (dd-conc-name defstruct) @@ -658,11 +662,18 @@ remove the ambiguity in your code.~@:>" accessor-name) (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))) - + ;; FIXME: It would be good to check for name collisions here, but + ;; the easy check, + ;;x#-sb-xc-host + ;;x(when (and (fboundp accessor-name) + ;;x (not (accessor-inherited-data accessor-name defstruct))) + ;;x (style-warn "redefining ~S in DEFSTRUCT" accessor-name))) + ;; which was done until sbcl-0.8.11.18 or so, is wrong: it causes + ;; a warning at MACROEXPAND time, when instead the warning should + ;; occur not just because the code was constructed, but because it + ;; is actually compiled or loaded. + ) + (when default-p (setf (dsd-default slot) default)) (when type-p @@ -674,8 +685,8 @@ (if read-only (setf (dsd-read-only slot) t) (when (dsd-read-only slot) - (error "Slot ~S is :READ-ONLY in parent and must be :READ-ONLY in subtype ~S." - name + (error "~@" (dsd-name slot))))) slot)) @@ -687,12 +698,7 @@ ;;; ;;; FIXME: This should use the data in *RAW-SLOT-DATA-LIST*. (defun structure-raw-slot-type-and-size (type) - (cond #+nil - (;; FIXME: For now we suppress raw slots, since there are various - ;; issues about the way that the cross-compiler handles them. - (not (boundp '*dummy-placeholder-to-stop-compiler-warnings*)) - (values nil nil nil)) - ((and (sb!xc:subtypep type '(unsigned-byte 32)) + (cond ((and (sb!xc:subtypep type '(unsigned-byte 32)) (multiple-value-bind (fixnum? fixnum-certain?) (sb!xc:subtypep type 'fixnum) ;; (The extra test for FIXNUM-CERTAIN? here is @@ -812,9 +818,16 @@ (dsd-index included-slot)) (dd-inherited-accessor-alist dd) :test #'eq :key #'car)) - (parse-1-dsd dd - modified - (copy-structure included-slot))))))) + (let ((new-slot (parse-1-dsd dd + modified + (copy-structure included-slot)))) + (when (and (neq (dsd-type new-slot) (dsd-type included-slot)) + (not (subtypep (dsd-type included-slot) + (dsd-type new-slot))) + (dsd-safe-p included-slot)) + (setf (dsd-safe-p new-slot) nil) + ;; XXX: notify? + ))))))) ;;;; various helper functions for setting up DEFSTRUCTs @@ -891,29 +904,44 @@ (multiple-value-bind (scaled-dsd-index misalignment) (floor (dsd-index dsd) raw-n-words) (aver (zerop misalignment)) - `(,raw-slot-accessor (,ref ,instance-name ,(dd-raw-index dd)) - ,scaled-dsd-index)))))) - -;;; Return inline expansion designators (i.e. values suitable for -;;; (INFO :FUNCTION :INLINE-EXPANSION-DESIGNATOR ..)) for the reader -;;; and writer functions of the slot described by DSD. -(defun slot-accessor-inline-expansion-designators (dd dsd) - (let ((instance-type-decl `(declare (type ,(dd-name dd) instance))) - (accessor-place-form (%accessor-place-form dd dsd 'instance)) + (let* ((raw-vector-bare-form + `(,ref ,instance-name ,(dd-raw-index dd))) + (raw-vector-form + (if (eq raw-type 'unsigned-byte) + (progn + (aver (= raw-n-words 1)) + (aver (eq raw-slot-accessor 'aref)) + ;; FIXME: when the 64-bit world rolls + ;; around, this will need to be reviewed, + ;; along with the whole RAW-SLOT thing. + `(truly-the (simple-array (unsigned-byte 32) (*)) + ,raw-vector-bare-form)) + raw-vector-bare-form))) + `(,raw-slot-accessor ,raw-vector-form ,scaled-dsd-index))))))) + +;;; Return source transforms for the reader and writer functions of +;;; the slot described by DSD. They should be inline expanded, but +;;; source transforms work faster. +(defun slot-accessor-transforms (dd dsd) + (let ((accessor-place-form (%accessor-place-form dd dsd + `(the ,(dd-name dd) instance))) (dsd-type (dsd-type dsd)) (value-the (if (dsd-safe-p dsd) 'truly-the 'the))) - (values (lambda () `(lambda (instance) - ,instance-type-decl - (,value-the ,dsd-type ,accessor-place-form))) - (lambda () `(lambda (new-value instance) - (declare (type ,dsd-type new-value)) - ,instance-type-decl - (setf ,accessor-place-form new-value)))))) + (values (sb!c:source-transform-lambda (instance) + `(,value-the ,dsd-type ,(subst instance 'instance + accessor-place-form))) + (sb!c:source-transform-lambda (new-value instance) + (destructuring-bind (accessor-name &rest accessor-args) + accessor-place-form + `(,(info :setf :inverse accessor-name) + ,@(subst instance 'instance accessor-args) + (the ,dsd-type ,new-value))))))) ;;; Return a LAMBDA form which can be used to set a slot. (defun slot-setter-lambda-form (dd dsd) - (funcall (nth-value 1 - (slot-accessor-inline-expansion-designators dd dsd)))) + `(lambda (new-value instance) + ,(funcall (nth-value 1 (slot-accessor-transforms dd dsd)) + '(dummy new-value instance)))) ;;; core compile-time setup of any class with a LAYOUT, used even by ;;; !DEFSTRUCT-WITH-ALTERNATE-METACLASS weirdosities @@ -974,15 +1002,15 @@ (let ((copier-name (dd-copier-name dd))) (when copier-name - (sb!xc:proclaim `(ftype (function (,dtype) ,dtype) ,copier-name)))) + (sb!xc:proclaim `(ftype (sfunction (,dtype) ,dtype) ,copier-name)))) (let ((predicate-name (dd-predicate-name dd))) (when predicate-name - (sb!xc:proclaim `(ftype (function (t) t) ,predicate-name)) + (sb!xc:proclaim `(ftype (sfunction (t) boolean) ,predicate-name)) ;; Provide inline expansion (or not). (ecase (dd-type dd) ((structure funcallable-structure) - ;; Let the predicate be inlined. + ;; Let the predicate be inlined. (setf (info :function :inline-expansion-designator predicate-name) (lambda () `(lambda (x) @@ -1008,31 +1036,24 @@ (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) + (slot-accessor-transforms dd dsd) + (sb!xc:proclaim `(ftype (sfunction (,dtype) ,dsd-type) ,accessor-name)) - (setf (info :function :inline-expansion-designator - accessor-name) - reader-designator - (info :function :inlinep accessor-name) - :inline) + (setf (info :function :source-transform accessor-name) + reader-designator) (unless (dsd-read-only dsd) (let ((setf-accessor-name `(setf ,accessor-name))) (sb!xc:proclaim - `(ftype (function (,dsd-type ,dtype) ,dsd-type) + `(ftype (sfunction (,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))))) + (setf (info :function :source-transform setf-accessor-name) + writer-designator))))) ((not (= (cdr inherited) (dsd-index dsd))) (style-warn "~@" accessor-name - (dsd-%name dsd))))))))) + (dsd-name dsd))))))))) (values)) ;;;; redefinition stuff @@ -1049,16 +1070,16 @@ (collect ((moved) (retyped)) (dolist (name (intersection onames nnames)) - (let ((os (find name oslots :key #'dsd-name)) - (ns (find name nslots :key #'dsd-name))) - (unless (subtypep (dsd-type ns) (dsd-type os)) + (let ((os (find name oslots :key #'dsd-name :test #'string=)) + (ns (find name nslots :key #'dsd-name :test #'string=))) + (unless (sb!xc:subtypep (dsd-type ns) (dsd-type os)) (retyped name)) (unless (and (= (dsd-index os) (dsd-index ns)) (eq (dsd-raw-type os) (dsd-raw-type ns))) (moved name)))) (values (moved) (retyped) - (set-difference onames nnames))))) + (set-difference onames nnames :test #'string=))))) ;;; If we are redefining a structure with different slots than in the ;;; currently loaded version, give a warning and return true. @@ -1441,17 +1462,26 @@ (unless (or defaults boas) (push (symbolicate "MAKE-" (dd-name defstruct)) defaults)) - (collect ((res)) + (collect ((res) (names)) (when defaults - (let ((cname (first defaults))) - (setf (dd-default-constructor defstruct) cname) - (res (create-keyword-constructor defstruct creator)) - (dolist (other-name (rest defaults)) - (res `(setf (fdefinition ',other-name) (fdefinition ',cname))) - (res `(declaim (ftype function ',other-name)))))) + (let ((cname (first defaults))) + (setf (dd-default-constructor defstruct) cname) + (res (create-keyword-constructor defstruct creator)) + (names cname) + (dolist (other-name (rest defaults)) + (res `(setf (fdefinition ',other-name) (fdefinition ',cname))) + (names other-name)))) (dolist (boa boas) - (res (create-boa-constructor defstruct boa creator))) + (res (create-boa-constructor defstruct boa creator)) + (names (first boa))) + + (res `(declaim (ftype + (sfunction * + ,(if (eq (dd-type defstruct) 'structure) + (dd-name defstruct) + '*)) + ,@(names)))) (res)))) @@ -1498,7 +1528,7 @@ (index 1)) (dolist (slot-name slot-names) (push (make-defstruct-slot-description - :%name (symbol-name slot-name) + :name slot-name :index index :accessor-name (symbolicate conc-name slot-name)) reversed-result) @@ -1588,7 +1618,8 @@ (let ((,object-gensym ,raw-maker-form)) ,@(mapcar (lambda (slot-name) (let ((dsd (find (symbol-name slot-name) dd-slots - :key #'dsd-%name + :key (lambda (x) + (symbol-name (dsd-name x))) :test #'string=))) ;; KLUDGE: bug 117 bogowarning. Neither ;; DECLAREing the type nor TRULY-THE cut