From: Bruno H. <br...@cl...> - 2004-06-14 13:04:38
|
Method combination types, such as STANDARD, that make use of MAKE-METHOD in the effective method form don't work when one of the methods is of user-defined type. Test case that works: (defclass user-method (standard-method) (myslot)) (defmacro def-user-method (name &rest rest) (let* ((lambdalist-position (position-if #'listp rest)) (qualifiers (subseq rest 0 lambdalist-position)) (lambdalist (elt rest lambdalist-position)) (body (subseq rest (+ lambdalist-position 1))) (required-part (subseq lambdalist 0 (or (position-if #'(lambda (x) (member x lambda-list-keywords)) lambdalist) (length lambdalist)))) (specializers (mapcar #'find-class (mapcar #'(lambda (x) (if (consp x) (second x) 't)) required-part))) (unspecialized-required-part (mapcar #'(lambda (x) (if (consp x) (first x) x)) required-part)) (unspecialized-lambdalist (append unspecialized-required-part (subseq required-part (length required-part))))) `(PROGN (ADD-METHOD #',name (MAKE-INSTANCE 'USER-METHOD :QUALIFIERS ',qualifiers :LAMBDA-LIST ',unspecialized-lambdalist :SPECIALIZERS ',specializers :FUNCTION #'(LAMBDA (ARGUMENTS NEXT-METHODS-LIST) (FLET ((NEXT-METHOD-P () NEXT-METHODS-LIST) (CALL-NEXT-METHOD (&REST NEW-ARGUMENTS) (UNLESS NEW-ARGUMENTS (SETQ NEW-ARGUMENTS ARGUMENTS)) (IF (NULL NEXT-METHODS-LIST) (ERROR "no next method for arguments ~:S" ARGUMENTS) (FUNCALL (#+SBCL SB-PCL:METHOD-FUNCTION #-SBCL METHOD-FUNCTION (FIRST NEXT-METHODS-LIST)) NEW-ARGUMENTS (REST NEXT-METHODS-LIST))))) (APPLY #'(LAMBDA ,unspecialized-lambdalist ,@body) ARGUMENTS))))) ',name))) (progn (defgeneric test-um03 (x)) (defmethod test-um03 ((x integer)) (list* 'integer x (not (null (next-method-p))) (call-next-method))) (def-user-method test-um03 ((x rational)) (list* 'rational x (not (null (next-method-p))) (call-next-method))) (defmethod test-um03 ((x real)) (list 'real x (not (null (next-method-p))))) (test-um03 17)) Two test cases that fail with different errors: ; First among three around methods. (progn (defgeneric test-um10 (x)) (defmethod test-um10 ((x integer)) (list* 'integer x (not (null (next-method-p))) (call-next-method))) (defmethod test-um10 ((x rational)) (list* 'rational x (not (null (next-method-p))) (call-next-method))) (defmethod test-um10 ((x real)) (list 'real x (not (null (next-method-p))))) (defmethod test-um10 :after ((x real))) (def-user-method test-um10 :around ((x integer)) (list* 'around-integer x (not (null (next-method-p))) (call-next-method))) (defmethod test-um10 :around ((x rational)) (list* 'around-rational x (not (null (next-method-p))) (call-next-method))) (defmethod test-um10 :around ((x real)) (list* 'around-real x (not (null (next-method-p))) (call-next-method))) (test-um10 17)) -> The value (MAKE-METHOD (MULTIPLE-VALUE-PROG1 (PROGN # #) (SB-PCL::CALL-METHOD-LIST #))) is not of type (OR FUNCTION NULL). ; Third among three around methods. (progn (defgeneric test-um12 (x)) (defmethod test-um12 ((x integer)) (list* 'integer x (not (null (next-method-p))) (call-next-method))) (defmethod test-um12 ((x rational)) (list* 'rational x (not (null (next-method-p))) (call-next-method))) (defmethod test-um12 ((x real)) (list 'real x (not (null (next-method-p))))) (defmethod test-um12 :after ((x real))) (defmethod test-um12 :around ((x integer)) (list* 'around-integer x (not (null (next-method-p))) (call-next-method))) (defmethod test-um12 :around ((x rational)) (list* 'around-rational x (not (null (next-method-p))) (call-next-method))) (def-user-method test-um12 :around ((x real)) (list* 'around-real x (not (null (next-method-p))) (call-next-method))) (test-um12 17)) -> There is no applicable method for the generic function #<STANDARD-GENERIC-FUNCTION SB-PCL:METHOD-FUNCTION (1)> when called with arguments ((MAKE-METHOD (MULTIPLE-VALUE-PROG1 # #))). Tested in sbcl-0.8.9 and sbcl-0.8.11. Bruno |
From: Christophe R. <cs...@ca...> - 2004-06-16 20:56:53
|
Bruno Haible <br...@cl...> writes: > Method combination types, such as STANDARD, that make use of MAKE-METHOD > in the effective method form don't work when one of the methods is of > user-defined type. Thanks for the report. > (unspecialized-lambdalist (append unspecialized-required-part (subseq required-part (length required-part))))) Though this doesn't affect sbcl's bugginess, you may wish to alter this to (unspecified-lambdalist (append unspecialized-required-part (subseq lambdalist (length required-part)))) that is, if I've understood what's going on. Cheers, Christophe -- http://www-jcsu.jesus.cam.ac.uk/~csr21/ +44 1223 510 299/+44 7729 383 757 (set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b))) (defvar b "~&Just another Lisp hacker~%") (pprint #36rJesusCollegeCambridge) |
From: Christophe R. <cs...@ca...> - 2006-08-05 12:36:55
|
Bruno Haible <br...@cl...> writes (in June 2004): > Method combination types, such as STANDARD, that make use of MAKE-METHOD > in the effective method form don't work when one of the methods is of > user-defined type. Thank you for the report. I have fixed this in sbcl-0.9.15.12, at least for those cases where MAKE-METHOD-LAMBDA has not been overridden. I believe that the case where a specialization of MAKE-METHOD-LAMBDA defines a method function to have more than the MOP-standard two arguments will still fail to honour MAKE-METHOD calls in the other argument positions to CALL-METHOD. > -> > The value > (MAKE-METHOD > (MULTIPLE-VALUE-PROG1 (PROGN # #) (SB-PCL::CALL-METHOD-LIST #))) > is not of type > (OR FUNCTION NULL). ^^^^^^^^^^^^^^^^^^ This error message, it turned out, was a second bug; functions accepting keyword arguments with non-constantp defaults would perform a preliminary typecheck as part of keyword argument processing, with any declared type augmented with NULL. Thus, code of the form (defstruct foo (a (lambda (x) x) :type function)) (make-foo :a 3) would give a type error with (OR FUNCTION NULL) as the expected-type. This has been fixed, in sbcl-0.9.15.11. Thanks again, Christophe |