* bug fix: (the [type] [constant]) now warns when [constant] matches
[type] except for the number of values. (Reported by Nathan Trapuzzano
on sbcl-help)
+ * bug fix: signal errors in required cases of slot-definition initialization
+ protocol. (lp#309072)
changes in sbcl-1.1.12 relative to sbcl-1.1.11:
* enhancement: Add sb-bsd-sockets:socket-shutdown, for calling
"CONDITION-SLOT-INITARGS"
"CONDITION-SLOT-INITFORM"
"CONDITION-SLOT-INITFORM-P"
+ "CONDITION-SLOT-INITFUNCTION"
"CONDITION-SLOT-NAME" "CONDITION-SLOT-READERS"
"CONDITION-SLOT-WRITERS"
(writers (missing-arg) :type list)
;; true if :INITFORM was specified
(initform-p (missing-arg) :type (member t nil))
- ;; If this is a function, call it with no args. Otherwise, it's the
- ;; actual value.
- (initform (missing-arg) :type t)
+ ;; the initform if :INITFORM was specified, otherwise NIL
+ (initform nil :type t)
+ ;; if this is a function, call it with no args to get the initform value
+ (initfunction (missing-arg) :type t)
;; allocation of this slot, or NIL until defaulted
(allocation nil :type (member :instance :class nil))
- ;; If ALLOCATION is :CLASS, this is a cons whose car holds the value.
+ ;; If ALLOCATION is :CLASS, this is a cons whose car holds the value
(cell nil :type (or cons null))
;; slot documentation
(documentation nil :type (or string null)))
;; Otherwise use the initform of SLOT, if there is one.
(if (condition-slot-initform-p slot)
- (let ((initform (condition-slot-initform slot)))
- (if (functionp initform)
- (funcall initform)
- initform))
+ (let ((initfun (condition-slot-initfunction slot)))
+ (aver (functionp initfun))
+ (funcall initfun))
(error "unbound condition slot: ~S" (condition-slot-name slot)))))
(defun find-condition-class-slot (condition-class slot-name)
(setf (condition-slot-initform-p found)
(condition-slot-initform-p sslot))
(setf (condition-slot-initform found)
- (condition-slot-initform sslot)))
+ (condition-slot-initform sslot))
+ (setf (condition-slot-initfunction sslot)
+ (condition-slot-initfunction found)))
(unless (condition-slot-allocation found)
(setf (condition-slot-allocation found)
(condition-slot-allocation sslot))))
(unless (condition-slot-cell slot)
(setf (condition-slot-cell slot)
(list (if (condition-slot-initform-p slot)
- (let ((initform (condition-slot-initform slot)))
- (if (functionp initform)
- (funcall initform)
- initform))
+ (let ((initfun (condition-slot-initfunction slot)))
+ (aver (functionp initfun))
+ (funcall initfun))
*empty-condition-slot*))))
(push slot (condition-classoid-class-slots class)))
((:instance nil)
(setf (condition-slot-allocation slot) :instance)
- (when (or (functionp (condition-slot-initform slot))
+ ;; FIXME: isn't this "always hairy"?
+ (when (or (functionp (condition-slot-initfunction slot))
(dolist (initarg (condition-slot-initargs slot) nil)
(when (functionp (third (assoc initarg e-def-initargs)))
(return t))))
:writers ',(writers)
:initform-p ',initform-p
:documentation ',documentation
- :initform ,(when initform-p
- `#'(lambda () ,initform))
+ :initform ,(when initform-p `',initform)
+ :initfunction ,(when initform-p
+ `#'(lambda () ,initform))
:allocation ',allocation)))))
(dolist (option options)
(set-val 'initform (get-val :initform))
(set-val 'initfunction (get-val :initfunction))
(set-val 'initargs (get-val :initargs))
- (set-val 'readers (get-val :readers))
- (set-val 'writers (get-val :writers))
+ (unless effective-p
+ (set-val 'readers (get-val :readers))
+ (set-val 'writers (get-val :writers)))
(set-val 'allocation :instance)
(set-val '%type (or (get-val :type) t))
(set-val '%documentation (or (get-val :documentation) ""))
:readers ,(condition-slot-readers slot)
:writers ,(condition-slot-writers slot)
,@(when (condition-slot-initform-p slot)
- (let ((form-or-fun (condition-slot-initform slot)))
- (if (functionp form-or-fun)
- `(:initfunction ,form-or-fun)
- `(:initform ,form-or-fun
- :initfunction ,(lambda () form-or-fun)))))
+ (let ((initform (condition-slot-initform slot))
+ (initfun (condition-slot-initfunction slot)))
+ `(:initform ',initform :initfunction ,initfun)))
:allocation ,(condition-slot-allocation slot)
:documentation ,(condition-slot-documentation slot))))
(cond ((structure-type-p name)
:initform nil
:initarg :initfunction
:accessor slot-definition-initfunction)
- (readers
- :initform nil
- :initarg :readers
- :accessor slot-definition-readers)
- (writers
- :initform nil
- :initarg :writers
- :accessor slot-definition-writers)
(initargs
:initform nil
:initarg :initargs
:accessor slot-definition-internal-writer-function)))
(defclass direct-slot-definition (slot-definition)
- ())
+ ((readers
+ :initform nil
+ :initarg :readers
+ :accessor slot-definition-readers)
+ (writers
+ :initform nil
+ :initarg :writers
+ :accessor slot-definition-writers)))
(defclass effective-slot-definition (slot-definition)
((accessor-flags
;;; lookup machinery.
(defvar *standard-classes*
+ ;; KLUDGE: order matters! finding effective slot definitions
+ ;; involves calling slot-definition-name, and we need to do that to
+ ;; break metacycles, so STANDARD-EFFECTIVE-SLOT-DEFINITION must
+ ;; precede STANDARD-DIRECT-SLOT-DEFINITION in this list, at least
+ ;; until ACCESSES-STANDARD-CLASS-SLOT-P is generalized
'(standard-method standard-generic-function standard-class
- standard-effective-slot-definition))
+ standard-effective-slot-definition standard-direct-slot-definition))
(defvar *standard-slot-locations* (make-hash-table :test 'equal))
(standard-slot-value slotd slot-name
*the-class-standard-effective-slot-definition*))
+(defun standard-slot-value/dslotd (slotd slot-name)
+ (standard-slot-value slotd slot-name
+ *the-class-standard-direct-slot-definition*))
+
(defun standard-slot-value/class (class slot-name)
(standard-slot-value class slot-name *the-class-standard-class*))
\f
;;; object of the slot accessed, and ACCESSOR-TYPE is one of the symbols
;;; READER or WRITER describing the slot access.
(defun accesses-standard-class-slot-p (gf)
- (flet ((standard-class-slot-access (gf class)
- (loop with gf-name = (standard-slot-value/gf gf 'name)
- for slotd in (standard-slot-value/class class 'slots)
- ;; FIXME: where does BOUNDP fit in here? Is it
- ;; relevant?
- as readers = (standard-slot-value/eslotd slotd 'readers)
- as writers = (standard-slot-value/eslotd slotd 'writers)
- if (member gf-name readers :test #'equal)
- return (values slotd 'reader)
- else if (member gf-name writers :test #'equal)
- return (values slotd 'writer))))
+ (labels
+ ((all-dslotds (class &aux done)
+ (labels ((all-dslotds-aux (class)
+ (if (or (member class done) (not (eq (class-of class) *the-class-standard-class*)))
+ nil
+ (progn
+ (push class done)
+ (append (standard-slot-value/class class 'direct-slots)
+ (mapcan #'(lambda (c)
+ (copy-list (all-dslotds-aux c)))
+ (standard-slot-value/class class 'direct-superclasses)))))))
+ (all-dslotds-aux class)))
+ (standard-class-slot-access (gf class)
+
+ (loop with gf-name = (standard-slot-value/gf gf 'name)
+ with eslotds = (standard-slot-value/class class 'slots)
+ with dslotds = (all-dslotds class)
+ for dslotd in dslotds
+ as readers = (standard-slot-value/dslotd dslotd 'readers)
+ as writers = (standard-slot-value/dslotd dslotd 'writers)
+ as name = (standard-slot-value/dslotd dslotd 'name)
+ as eslotd = (find name eslotds :key (lambda (x) (standard-slot-value/eslotd x 'name)))
+ if (member gf-name readers :test #'equal)
+ return (values eslotd 'reader)
+ else if (member gf-name writers :test #'equal)
+ return (values eslotd 'writer))))
(dolist (class-name *standard-classes*)
(let ((class (find-class class-name)))
(multiple-value-bind (slotd accessor-type)
*the-class-global-writer-method*
*the-class-global-boundp-method*
*the-class-standard-generic-function*
+ *the-class-standard-direct-slot-definition*
*the-class-standard-effective-slot-definition*
*the-eslotd-standard-class-slots*
(setf (gethash type **typecheck-cache**) fun
(slot-info-typecheck info) fun))))))))
+(define-condition slotd-initialization-error (reference-condition error)
+ ((initarg :initarg :initarg :reader slotd-initialization-error-initarg)
+ (kind :initarg :kind :reader slotd-initialization-error-kind)
+ (value :initarg :value :initform nil :reader slotd-initialization-error-value))
+ (:default-initargs :references (list '(:amop :initialization slot-definition)))
+ (:report (lambda (condition stream)
+ (let ((initarg (slotd-initialization-error-initarg condition))
+ (kind (slotd-initialization-error-kind condition))
+ (value (slotd-initialization-error-value condition)))
+ (format stream
+ "~@<Invalid ~S initialization: the initialization ~
+ argument ~S was ~
+ ~[missing~*~;not a symbol: ~S~;constant: ~S~].~@:>"
+ 'slot-definition initarg
+ (getf '(:missing 0 :symbol 1 :constant 2) kind)
+ value)))))
+
+(define-condition slotd-initialization-type-error (slotd-initialization-error type-error)
+ ((value :initarg :datum))
+ (:report (lambda (condition stream)
+ (let ((initarg (slotd-initialization-error-initarg condition))
+ (datum (type-error-datum condition))
+ (expected-type (type-error-expected-type condition)))
+ (format stream
+ "~@<Invalid ~S initialization: the initialization ~
+ argument ~S was ~S, which is not of type ~S.~@:>"
+ 'slot-definition initarg
+ datum expected-type)))))
+
+(defmethod initialize-instance :before ((slotd slot-definition)
+ &key (name nil namep)
+ (initform nil initformp)
+ (initfunction nil initfunp)
+ (type nil typep)
+ (allocation nil allocationp)
+ (initargs nil initargsp)
+ (documentation nil docp))
+ (unless namep
+ (error 'slotd-initialization-error :initarg :name :kind :missing))
+ (unless (symbolp name)
+ (error 'slotd-initialization-type-error :initarg :name :datum name :expected-type 'symbol))
+ (when (constantp name)
+ (error 'slotd-initialization-error :initarg :name :kind :constant :value name))
+ (when (and initformp (not initfunp))
+ (error 'slotd-initialization-error :initarg :initfunction :kind :missing))
+ (when (and initfunp (not initformp))
+ (error 'slotd-initialization-error :initarg :initform :kind :missing))
+ (when (and typep (not t))
+ ;; FIXME: do something. Need SYNTACTICALLY-VALID-TYPE-SPECIFIER-P
+ )
+ (when (and allocationp (not (symbolp allocation)))
+ (error 'slotd-initialization-type-error :initarg :allocation :datum allocation :expected-type 'symbol))
+ (when initargsp
+ (unless (typep initargs 'list)
+ (error 'slotd-initialization-type-error :initarg :initarg :datum initargs :expected-type 'list))
+ (do ((is initargs (cdr is)))
+ ((atom is)
+ (unless (null is)
+ (error 'slotd-initialization-type-error :initarg :initarg :datum initargs :expected-type '(satisfies proper-list-p))))
+ (unless (symbolp (car is))
+ (error 'slotd-initialization-type-error :initarg :initarg :datum is :expected-type '(or null (cons symbol))))))
+ (when docp
+ (unless (typep documentation '(or null string))
+ (error 'slotd-initialization-type-error :initarg :documentation :datum documentation :expected-type '(or null string)))))
+
+(defmethod initialize-instance :before ((dslotd direct-slot-definition)
+ &key
+ (readers nil readersp)
+ (writers nil writersp))
+ (macrolet ((check (arg argp)
+ `(when ,argp
+ (unless (typep ,arg 'list)
+ (error 'slotd-initialization-type-error
+ :initarg ,(keywordicate arg)
+ :datum ,arg :expected-type 'list))
+ (do ((as ,arg (cdr as)))
+ ((atom as)
+ (unless (null as)
+ (error 'slotd-initialization-type-error
+ :initarg ,(keywordicate arg)
+ :datum ,arg :expected-type '(satisfies proper-list-p))))
+ (unless (valid-function-name-p (car as))
+ (error 'slotd-initialization-type-error
+ :initarg ,(keywordicate arg)
+ :datum ,arg :expected-type '(or null (cons (satisfies valid-function-name-p)))))))))
+ (check readers readersp)
+ (check writers writersp)))
+
(defmethod initialize-instance :after ((slotd effective-slot-definition) &key)
(let ((info (make-slot-info :slotd slotd)))
(generate-slotd-typecheck slotd info)
(ensure-generic-function 'make-instance :method-combination mc))
;; Let's make sure the list works too...
(ensure-generic-function 'make-instance :method-combination '(standard)))
+
+(with-test (:name :bug-309072)
+ ;; original reported test cases
+ (raises-error? (make-instance 'sb-mop:slot-definition))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'pi))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 3))
+ ;; extra cases from the MOP dictionary
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'x
+ :initform nil))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'x
+ :initfunction (lambda () nil)))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'x
+ :initfunction (lambda () nil)))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'x
+ :allocation ""))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'x
+ :initargs ""))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'x
+ :initargs '(foo . bar)))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'x
+ :initargs '(foo bar 3)))
+ (raises-error? (make-instance 'sb-mop:slot-definition :name 'x
+ :documentation '(())))
+ ;; distinction between DIRECT- and EFFECTIVE- slot definitions
+ (raises-error? (make-instance 'sb-mop:effective-slot-definition
+ :name 'x :readers '(foo)))
+ (raises-error? (make-instance 'sb-mop:effective-slot-definition
+ :name 'x :writers '(foo)))
+ (make-instance 'sb-mop:direct-slot-definition
+ :name 'x :readers '(foo))
+ (make-instance 'sb-mop:direct-slot-definition
+ :name 'x :writers '(foo))
+ (raises-error? (make-instance 'sb-mop:direct-slot-definition
+ :name 'x :readers ""))
+ (raises-error? (make-instance 'sb-mop:direct-slot-definition
+ :name 'x :readers '(3)))
+ (raises-error? (make-instance 'sb-mop:direct-slot-definition
+ :name 'x :readers '(foo . bar)))
+ (raises-error? (make-instance 'sb-mop:direct-slot-definition
+ :name 'x :writers ""))
+ (raises-error? (make-instance 'sb-mop:direct-slot-definition
+ :name 'x :writers '(3)))
+ (raises-error? (make-instance 'sb-mop:direct-slot-definition
+ :name 'x :writers '(foo . bar))))