-360: CALL-METHOD not recognized in method-combination body
- (reported by Bruno Haible)
- This method combination, which adds 'redo' and 'return' restarts for each
- method invocation to standard method combination, gives an error in SBCL.
- (defun prompt-for-new-values ()
- (format *debug-io* "~&New values: ")
- (list (read *debug-io*)))
- (defun add-method-restarts (form method)
- (let ((block (gensym))
- (tag (gensym)))
- `(BLOCK ,block
- (TAGBODY
- ,tag
- (RETURN-FROM ,block
- (RESTART-CASE ,form
- (METHOD-REDO ()
- :REPORT (LAMBDA (STREAM) (FORMAT STREAM "Try calling ~S again." ,method))
- (GO ,tag))
- (METHOD-RETURN (L)
- :REPORT (LAMBDA (STREAM) (FORMAT STREAM "Specify return values for ~S call." ,method))
- :INTERACTIVE (LAMBDA () (PROMPT-FOR-NEW-VALUES))
- (RETURN-FROM ,block (VALUES-LIST L)))))))))
- (defun convert-effective-method (efm)
- (if (consp efm)
- (if (eq (car efm) 'CALL-METHOD)
- (let ((method-list (third efm)))
- (if (or (typep (first method-list) 'method) (rest method-list))
- ; Reduce the case of multiple methods to a single one.
- ; Make the call to the next-method explicit.
- (convert-effective-method
- `(CALL-METHOD ,(second efm)
- ((MAKE-METHOD
- (CALL-METHOD ,(first method-list) ,(rest method-list))))))
- ; Now the case of at most one method.
- (if (typep (second efm) 'method)
- ; Wrap the method call in a RESTART-CASE.
- (add-method-restarts
- (cons (convert-effective-method (car efm))
- (convert-effective-method (cdr efm)))
- (second efm))
- ; Normal recursive processing.
- (cons (convert-effective-method (car efm))
- (convert-effective-method (cdr efm))))))
- (cons (convert-effective-method (car efm))
- (convert-effective-method (cdr efm))))
- efm))
- (define-method-combination standard-with-restarts ()
- ((around (:around))
- (before (:before))
- (primary () :required t)
- (after (:after)))
- (flet ((call-methods-sequentially (methods)
- (mapcar #'(lambda (method)
- `(CALL-METHOD ,method))
- methods)))
- (let ((form (if (or before after (rest primary))
- `(MULTIPLE-VALUE-PROG1
- (PROGN
- ,@(call-methods-sequentially before)
- (CALL-METHOD ,(first primary) ,(rest primary)))
- ,@(call-methods-sequentially (reverse after)))
- `(CALL-METHOD ,(first primary)))))
- (when around
- (setq form
- `(CALL-METHOD ,(first around)
- (,@(rest around) (MAKE-METHOD ,form)))))
- (convert-effective-method form))))
- (defgeneric testgf16 (x) (:method-combination standard-with-restarts))
- (defclass testclass16a () ())
- (defclass testclass16b (testclass16a) ())
- (defclass testclass16c (testclass16a) ())
- (defclass testclass16d (testclass16b testclass16c) ())
- (defmethod testgf16 ((x testclass16a))
- (list 'a
- (not (null (find-restart 'method-redo)))
- (not (null (find-restart 'method-return)))))
- (defmethod testgf16 ((x testclass16b))
- (cons 'b (call-next-method)))
- (defmethod testgf16 ((x testclass16c))
- (cons 'c (call-next-method)))
- (defmethod testgf16 ((x testclass16d))
- (cons 'd (call-next-method)))
- (testgf16 (make-instance 'testclass16d))
-
- Expected: (D B C A T T)
- Got: ERROR CALL-METHOD outside of a effective method form
-
- This is a bug because ANSI CL HyperSpec/Body/locmac_call-m__make-method
- says
- "The macro call-method invokes the specified method, supplying it with
- arguments and with definitions for call-next-method and for next-method-p.
- If the invocation of call-method is lexically inside of a make-method,
- the arguments are those that were supplied to that method. Otherwise
- the arguments are those that were supplied to the generic function."
- and the example uses nothing more than these two cases (as you can see by
- doing (trace convert-effective-method)).
-
-361: initialize-instance of standard-reader-method ignores :function argument
- (reported by Bruno Haible)
- Pass a custom :function argument to initialize-instance of a
- standard-reader-method instance, but it has no effect.
- ;; Check that it's possible to define reader methods that do typechecking.
- (progn
- (defclass typechecking-reader-method (sb-pcl:standard-reader-method)
- ())
- (defmethod initialize-instance ((method typechecking-reader-method) &rest initargs
- &key slot-definition)
- (let ((name (sb-pcl:slot-definition-name slot-definition))
- (type (sb-pcl:slot-definition-type slot-definition)))
- (apply #'call-next-method method
- :function #'(lambda (args next-methods)
- (declare (ignore next-methods))
- (apply #'(lambda (instance)
- (let ((value (slot-value instance name)))
- (unless (typep value type)
- (error "Slot ~S of ~S is not of type ~S: ~S"
- name instance type value))
- value))
- args))
- initargs)))
- (defclass typechecking-reader-class (standard-class)
- ())
- (defmethod sb-pcl:validate-superclass ((c1 typechecking-reader-class) (c2 standard-class))
- t)
- (defmethod reader-method-class ((class typechecking-reader-class) direct-slot &rest args)
- (find-class 'typechecking-reader-method))
- (defclass testclass25 ()
- ((pair :type (cons symbol (cons symbol null)) :initarg :pair :accessor testclass25-pair))
- (:metaclass typechecking-reader-class))
- (macrolet ((succeeds (form)
- `(not (nth-value 1 (ignore-errors ,form)))))
- (let ((p (list 'abc 'def))
- (x (make-instance 'testclass25)))
- (list (succeeds (make-instance 'testclass25 :pair '(seventeen 17)))
- (succeeds (setf (testclass25-pair x) p))
- (succeeds (setf (second p) 456))
- (succeeds (testclass25-pair x))
- (succeeds (slot-value x 'pair))))))
- Expected: (t t t nil t)
- Got: (t t t t t)
-
- (inspect (first (sb-pcl:generic-function-methods #'testclass25-pair)))
- shows that the method was created with a FAST-FUNCTION slot but with a
- FUNCTION slot of NIL.
-