1.0.6.12: Improve user-subclassed SB-MOP:SPECIALIZER support
authorChristophe Rhodes <csr21@cantab.net>
Sat, 2 Jun 2007 09:04:10 +0000 (09:04 +0000)
committerChristophe Rhodes <csr21@cantab.net>
Sat, 2 Jun 2007 09:04:10 +0000 (09:04 +0000)
In order to support surface syntax for users in DEFMETHOD,
define and bootstrap a new SB-PCL:MAKE-METHOD-SPECIALIZERS-FORM
(a bit like SB-MOP:MAKE-METHOD-LAMBDA).  This or something like
it is fundamentally necessary to support EQL specializers, which
as syntax have a bit which is syntax and a bit which is
evaluated in the lexical environment of the DEFMETHOD form.

Bootstrapping it is a little tedious, but (eventually) work out
where parsed specializers can be made and where we don't yet
have FIND-CLASS in the bootstrap.  In the course of that, note
that we need to be able to parse and unparse specializers at
runtime: parsing to support FIND-METHOD; unparsing to get nice
names for our method functions in backtraces.  Define an
experimental interface for that, too (see NEWS).

In the process, fix a couple of bugs in NO-NEXT-METHOD handling;
it is not safe to assume that the compile-time method name can
be used to find the method object.  Instead, fight a little bit
with the file compiler to have a cons cell shared between the
method function and the method initargs, and arrange to have the
cell's CAR be set to the method once it is created; then we can
do NO-NEXT-METHOD sanely.

Tests for the no-next-method stuff, and adjust wonky find-method
specializer arguments in other tests.

16 files changed:
NEWS
contrib/sb-introspect/sb-introspect.lisp
package-data-list.lisp-expr
src/code/late-condition.lisp
src/code/ntrace.lisp
src/pcl/boot.lisp
src/pcl/defs.lisp
src/pcl/describe.lisp
src/pcl/env.lisp
src/pcl/fixup.lisp
src/pcl/generic-functions.lisp
src/pcl/methods.lisp
src/pcl/print-object.lisp
src/pcl/slots-boot.lisp
tests/clos-1.impure.lisp
version.lisp-expr

diff --git a/NEWS b/NEWS
index 8a8c26a..3a152a4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,12 @@
 ;;;; -*- coding: utf-8; -*-
 changes in sbcl-1.0.7 relative to sbcl-1.0.6:
+  * MOP improvement: support for user-defined subclasses of
+    SB-MOP:SPECIALIZER has been enhanced.  The experimental interface
+    function SB-PCL:MAKE-METHOD-SPECIALIZERS-FORM, called as part of
+    the expansion of DEFMETHOD, is responsible for generating a form
+    which creates a list of specializers when evaluated.  Additional
+    functions SB-PCL:[UN]PARSE-SPECIALIZER-USING-CLASS provide
+    debugging and introspective support.
   * minor incompatible change: the (unsupported) spinlock interface
     has changed: free spinlock now has the value NIL, and a held spinlock
     has the owning thread as its value.
index e54f5c4..6f1ef01 100644 (file)
@@ -320,8 +320,11 @@ If an unsupported TYPE is requested, the function will return NIL.
        (when source
          (setf (definition-source-description source)
                (append (method-qualifiers object)
-                       (sb-pcl::unparse-specializers
-                        (sb-mop:method-specializers object)))))
+                       (if (sb-mop:method-generic-function object)
+                           (sb-pcl::unparse-specializers
+                            (sb-mop:method-generic-function object)
+                            (sb-mop:method-specializers object))
+                           (sb-mop:method-specializers object)))))
        source))
     #+sb-eval
     (sb-eval:interpreted-function
index 70370cc..850288c 100644 (file)
@@ -1787,6 +1787,10 @@ versions of SBCL, and the other stuff in here is definitely not
 guaranteed to be present in later versions of SBCL.  Use of this
 package is deprecated in favour of SB-MOP."
       :use ("CL" "SB!MOP" "SB!INT" "SB!EXT" "SB!WALKER" "SB!KERNEL")
+      ;; experimental SBCL-only (for now) symbols
+      :export ("MAKE-METHOD-SPECIALIZERS-FORM"
+               "PARSE-SPECIALIZER-USING-CLASS"
+               "UNPARSE-SPECIALIZER-USING-CLASS")
       ;; FIXME: After a little while, these reexports can probably go
       ;; away, as they're superseded by the use of SB-MOP as the
       ;; publically-accessible package.
index 622051b..71c570d 100644 (file)
@@ -23,7 +23,7 @@
          (sb-mop:class-prototype (sb-mop:generic-function-method-class proto-gf))
          lambda
          env)
-      `(values #',lambda ,initargs))))
+      `(values #',lambda ',initargs))))
 
 (defun install-condition-slot-reader (name condition slot-name)
   (let ((gf (if (fboundp name)
index a400ff7..4404d66 100644 (file)
       (setf (gethash fun *traced-funs*) info))
 
     (when (and (typep fun 'generic-function)
-               (trace-info-methods info))
-      (dolist (method-name (sb-pcl::list-all-maybe-method-names fun))
-        (when (fboundp method-name)
+               (trace-info-methods info)
+               ;; we are going to trace the method functions directly.
+               (not (trace-info-encapsulated info)))
+      (dolist (method (sb-mop:generic-function-methods fun))
+        (let ((mf (sb-mop:method-function method)))
           ;; NOTE: this direct style of tracing methods -- tracing the
           ;; pcl-internal method functions -- is only one possible
           ;; alternative.  It fails (a) when encapulation is
           ;; requested, because the function objects themselves are
           ;; stored in the method object; (b) when the method in
           ;; question is particularly simple, when the method
-          ;; functionality is in the dfun.  There is an alternative
-          ;; technique: to replace any currently active methods with
-          ;; methods which encapsulate the current one.  Steps towards
-          ;; this are currently commented out in src/pcl/env.lisp.  --
-          ;; CSR, 2005-01-03
-          (trace-1 method-name info)))))
+          ;; functionality is in the dfun.  See src/pcl/env.lisp for a
+          ;; stub implementation of encapsulating through a
+          ;; traced-method class.
+          (trace-1 mf info)
+          (when (typep mf 'sb-pcl::%method-function)
+            (trace-1 (sb-pcl::%method-function-fast-function mf) info))))))
 
   function-or-name)
 \f
index 8d87432..c8f9912 100644 (file)
@@ -68,18 +68,13 @@ bootstrapping.
 
 |#
 
-(declaim (notinline make-a-method
-                    add-named-method
+(declaim (notinline make-a-method add-named-method
                     ensure-generic-function-using-class
-                    add-method
-                    remove-method))
+                    add-method remove-method))
 
 (defvar *!early-functions*
-        '((make-a-method early-make-a-method
-                         real-make-a-method)
-          (add-named-method early-add-named-method
-                            real-add-named-method)
-          ))
+  '((make-a-method early-make-a-method real-make-a-method)
+    (add-named-method early-add-named-method real-add-named-method)))
 
 ;;; For each of the early functions, arrange to have it point to its
 ;;; early definition. Do this in a way that makes sure that if we
@@ -97,11 +92,14 @@ bootstrapping.
 ;;; *!GENERIC-FUNCTION-FIXUPS* is used by !FIX-EARLY-GENERIC-FUNCTIONS
 ;;; to convert the few functions in the bootstrap which are supposed
 ;;; to be generic functions but can't be early on.
+;;;
+;;; each entry is a list of name and lambda-list, class names as
+;;; specializers, and method body function name.
 (defvar *!generic-function-fixups*
   '((add-method
-     ((generic-function method)  ;lambda-list
-      (standard-generic-function method) ;specializers
-      real-add-method))          ;method-function
+     ((generic-function method)
+      (standard-generic-function method)
+      real-add-method))
     (remove-method
      ((generic-function method)
       (standard-generic-function method)
@@ -125,6 +123,18 @@ bootstrapping.
      ((proto-generic-function proto-method lambda-expression environment)
       (standard-generic-function standard-method t t)
       real-make-method-lambda))
+    (make-method-specializers-form
+     ((proto-generic-function proto-method specializer-names environment)
+      (standard-generic-function standard-method t t)
+      real-make-method-specializers-form))
+    (parse-specializer-using-class
+     ((generic-function specializer)
+      (standard-generic-function t)
+      real-parse-specializer-using-class))
+    (unparse-specializer-using-class
+     ((generic-function specializer)
+      (standard-generic-function t)
+      real-unparse-specializer-using-class))
     (make-method-initargs-form
      ((proto-generic-function proto-method
                               lambda-expression
@@ -358,11 +368,11 @@ bootstrapping.
       (add-method-declarations name qualifiers lambda-list body env)
     (multiple-value-bind (method-function-lambda initargs)
         (make-method-lambda proto-gf proto-method method-lambda env)
-      (let ((initargs-form (make-method-initargs-form proto-gf
-                                                      proto-method
-                                                      method-function-lambda
-                                                      initargs
-                                                      env)))
+      (let ((initargs-form (make-method-initargs-form
+                            proto-gf proto-method method-function-lambda
+                            initargs env))
+            (specializers-form (make-method-specializers-form
+                                proto-gf proto-method specializers env)))
         `(progn
           ;; Note: We could DECLAIM the ftype of the generic function
           ;; here, since ANSI specifies that we create it if it does
@@ -371,7 +381,7 @@ bootstrapping.
           ;; generic function has an explicit DEFGENERIC and any typos
           ;; in DEFMETHODs are warned about. Otherwise
           ;;
-          ;;   (DEFGENERIC FOO-BAR-BLETCH ((X T)))
+          ;;   (DEFGENERIC FOO-BAR-BLETCH (X))
           ;;   (DEFMETHOD FOO-BAR-BLETCH ((X HASH-TABLE)) ..)
           ;;   (DEFMETHOD FOO-BRA-BLETCH ((X SIMPLE-VECTOR)) ..)
           ;;   (DEFMETHOD FOO-BAR-BLETCH ((X VECTOR)) ..)
@@ -383,7 +393,7 @@ bootstrapping.
           ;; to VECTOR) but still doesn't do what was intended. I hate
           ;; that kind of bug (code which silently gives the wrong
           ;; answer), so we don't do a DECLAIM here. -- WHN 20000229
-          ,(make-defmethod-form name qualifiers specializers
+          ,(make-defmethod-form name qualifiers specializers-form
                                 unspecialized-lambda-list
                                 (if proto-method
                                     (class-name (class-of proto-method))
@@ -417,9 +427,20 @@ bootstrapping.
              (consp (setq fn (caddr initargs-form)))
              (eq (car fn) 'function)
              (consp (setq fn-lambda (cadr fn)))
-             (eq (car fn-lambda) 'lambda))
+             (eq (car fn-lambda) 'lambda)
+             (bug "Really got here"))
         (let* ((specls (mapcar (lambda (specl)
                                  (if (consp specl)
+                                     ;; CONSTANT-FORM-VALUE?  What I
+                                     ;; kind of want to know, though,
+                                     ;; is what happens if we don't do
+                                     ;; this for some slow-method
+                                     ;; function because of a hairy
+                                     ;; lexenv -- is the only bad
+                                     ;; effect that the method
+                                     ;; function ends up unnamed?  If
+                                     ;; so, couldn't we arrange to
+                                     ;; name it later?
                                      `(,(car specl) ,(eval (cadr specl)))
                                    specl))
                                specializers))
@@ -437,6 +458,8 @@ bootstrapping.
                        ,@(cdddr initargs-form)))))
         (make-defmethod-form-internal
          name qualifiers
+         specializers
+         #+nil
          `(list ,@(mapcar (lambda (specializer)
                             (if (consp specializer)
                                 ``(,',(car specializer)
@@ -529,6 +552,68 @@ bootstrapping.
   (declare (ignore proto-gf proto-method))
   (make-method-lambda-internal method-lambda env))
 
+(unless (fboundp 'make-method-lambda)
+  (setf (gdefinition 'make-method-lambda)
+        (symbol-function 'real-make-method-lambda)))
+
+(defun real-make-method-specializers-form
+    (proto-gf proto-method specializer-names env)
+  (declare (ignore env proto-gf proto-method))
+  (flet ((parse (name)
+           (cond
+             ((and (eq *boot-state* 'complete)
+                   (specializerp name))
+              name)
+             ((symbolp name) `(find-class ',name))
+             ((consp name) (ecase (car name)
+                             ((eql) `(intern-eql-specializer ,(cadr name)))
+                             ((class-eq) `(class-eq-specializer (find-class ',(cadr name))))
+                             ((prototype) `(fixme))))
+             (t (bug "Foo")))))
+    `(list ,@(mapcar #'parse specializer-names))))
+
+(unless (fboundp 'make-method-specializers-form)
+  (setf (gdefinition 'make-method-specializers-form)
+        (symbol-function 'real-make-method-specializers-form)))
+
+(defun real-parse-specializer-using-class (generic-function specializer)
+  (let ((result (specializer-from-type specializer)))
+    (if (specializerp result)
+        result
+        (error "~@<~S cannot be parsed as a specializer for ~S.~@:>"
+               specializer generic-function))))
+
+(unless (fboundp 'parse-specializer-using-class)
+  (setf (gdefinition 'parse-specializer-using-class)
+        (symbol-function 'real-parse-specializer-using-class)))
+
+(defun real-unparse-specializer-using-class (generic-function specializer)
+  (if (specializerp specializer)
+      ;; FIXME: this HANDLER-CASE is a bit of a hammer to crack a nut:
+      ;; the idea is that we want to unparse permissively, so that the
+      ;; lazy (or rather the "portable") specializer extender (who
+      ;; does not define methods on these new SBCL-specific MOP
+      ;; functions) can still subclass specializer and define methods
+      ;; without everything going wrong.  Making it cleaner and
+      ;; clearer that that is what we are defending against would be
+      ;; nice.  -- CSR, 2007-06-01
+      (handler-case
+          (let ((type (specializer-type specializer)))
+            (if (and (consp type) (eq (car type) 'class))
+                (let* ((class (cadr type))
+                       (class-name (class-name class)))
+                  (if (eq class (find-class class-name nil))
+                      class-name
+                      type))
+                type))
+        (error () specializer))
+      (error "~@<~S is not a legal specializer for ~S.~@:>"
+             specializer generic-function)))
+
+(unless (fboundp 'unparse-specializer-using-class)
+  (setf (gdefinition 'unparse-specializer-using-class)
+        (symbol-function 'real-unparse-specializer-using-class)))
+
 ;;; a helper function for creating Python-friendly type declarations
 ;;; in DEFMETHOD forms
 (defun parameter-specializer-declaration-in-defmethod (parameter specializer)
@@ -672,7 +757,15 @@ bootstrapping.
            (sll-decl (get-declaration '%method-lambda-list declarations))
            (method-name (when (consp name-decl) (car name-decl)))
            (generic-function-name (when method-name (car method-name)))
-           (specialized-lambda-list (or sll-decl (cadr method-lambda))))
+           (specialized-lambda-list (or sll-decl (cadr method-lambda)))
+           ;; the method-cell is a way of communicating what method a
+           ;; method-function implements, for the purpose of
+           ;; NO-NEXT-METHOD.  We need something that can be shared
+           ;; between function and initargs, but not something that
+           ;; will be coalesced as a constant (because we are naughty,
+           ;; oh yes) with the expansion of any other methods in the
+           ;; same file.  -- CSR, 2007-05-30
+           (method-cell (list (make-symbol "METHOD-CELL"))))
       (multiple-value-bind (parameters lambda-list specializers)
           (parse-specialized-lambda-list specialized-lambda-list)
         (let* ((required-parameters
@@ -792,14 +885,7 @@ bootstrapping.
                                            ,call-next-method-p
                                            :next-method-p-p ,next-method-p-p
                                            :setq-p ,setq-p
-                                           ;; we need to pass this along
-                                           ;; so that NO-NEXT-METHOD can
-                                           ;; be given a suitable METHOD
-                                           ;; argument; we need the
-                                           ;; QUALIFIERS and SPECIALIZERS
-                                           ;; inside the declaration to
-                                           ;; give to FIND-METHOD.
-                                           :method-name-declaration ,name-decl
+                                           :method-cell ,method-cell
                                            :closurep ,closurep
                                            :applyp ,applyp)
                            ,@walked-declarations
@@ -811,14 +897,9 @@ bootstrapping.
                                (declare (enable-package-locks
                                          %parameter-binding-modified))
                                ,@walked-lambda-body))))
-                      `(,@(when plist
-                                `(plist ,plist))
-                          ,@(when documentation
-                                  `(:documentation ,documentation)))))))))))
-
-(unless (fboundp 'make-method-lambda)
-  (setf (gdefinition 'make-method-lambda)
-        (symbol-function 'real-make-method-lambda)))
+                      `(,@(when call-next-method-p `(method-cell ,method-cell))
+                        ,@(when plist `(plist ,plist))
+                        ,@(when documentation `(:documentation ,documentation)))))))))))
 
 (defmacro simple-lexical-method-functions ((lambda-list
                                             method-args
@@ -844,7 +925,7 @@ bootstrapping.
 
 (defmacro bind-simple-lexical-method-functions
     ((method-args next-methods (&key call-next-method-p next-method-p-p setq-p
-                                     closurep applyp method-name-declaration))
+                                     closurep applyp method-cell))
      &body body
      &environment env)
   (if (not (or call-next-method-p setq-p closurep next-method-p-p applyp))
@@ -859,7 +940,7 @@ bootstrapping.
                           ,@(if (safe-code-p env)
                                 `((%check-cnm-args cnm-args
                                                    ,method-args
-                                                   ',method-name-declaration))
+                                                   ',method-cell))
                                 nil)
                           (if .next-method.
                               (funcall (if (std-instance-p .next-method.)
@@ -868,25 +949,18 @@ bootstrapping.
                                        (or cnm-args ,method-args)
                                        ,next-methods)
                               (apply #'call-no-next-method
-                                     ',method-name-declaration
+                                     ',method-cell
                                      (or cnm-args ,method-args))))))
                 ,@(and next-method-p-p
                        '((next-method-p ()
                           (not (null .next-method.))))))
            ,@body))))
 
-(defun call-no-next-method (method-name-declaration &rest args)
-  (destructuring-bind (name) method-name-declaration
-    (destructuring-bind (name &rest qualifiers-and-specializers) name
-      ;; KLUDGE: inefficient traversal, but hey.  This should only
-      ;; happen on the slow error path anyway.
-      (let* ((qualifiers (butlast qualifiers-and-specializers))
-             (specializers (car (last qualifiers-and-specializers)))
-             (method (find-method (gdefinition name) qualifiers specializers)))
-        (apply #'no-next-method
-               (method-generic-function method)
-               method
-               args)))))
+(defun call-no-next-method (method-cell &rest args)
+  (let ((method (car method-cell)))
+    (aver method)
+    (apply #'no-next-method (method-generic-function method)
+           method args)))
 
 (defstruct (method-call (:copier nil))
   (function #'identity :type function)
@@ -1162,7 +1236,7 @@ bootstrapping.
 \f
 
 (defmacro fast-call-next-method-body ((args next-method-call rest-arg)
-                                      method-name-declaration
+                                      method-cell
                                       cnm-args)
   `(if ,next-method-call
        ,(let ((call `(invoke-narrow-effective-method-function
@@ -1177,7 +1251,7 @@ bootstrapping.
                               ,cnm-args)
                     ,call)
                   ,call))
-       (call-no-next-method ',method-name-declaration
+       (call-no-next-method ',method-cell
                             ,@args
                             ,@(when rest-arg
                                     `(,rest-arg)))))
@@ -1186,7 +1260,7 @@ bootstrapping.
     ((args rest-arg next-method-call (&key
                                       call-next-method-p
                                       setq-p
-                                      method-name-declaration
+                                      method-cell
                                       next-method-p-p
                                       closurep
                                       applyp))
@@ -1204,13 +1278,13 @@ bootstrapping.
                                      (optimize (sb-c:insert-step-conditions 0)))
                            ,@(if (safe-code-p env)
                                  `((%check-cnm-args cnm-args (list ,@args)
-                                                    ',method-name-declaration))
+                                                    ',method-cell))
                                  nil)
                            (fast-call-next-method-body (,args
                                                         ,next-method-call
                                                         ,rest-arg)
-                                                        ,method-name-declaration
-                                                       cnm-args))))
+                            ,method-cell
+                            cnm-args))))
                 ,@(when next-method-p-p
                         `((next-method-p ()
                            (declare (optimize (sb-c:insert-step-conditions 0)))
@@ -1234,9 +1308,9 @@ bootstrapping.
 ;;; for COMPUTE-APPLICABLE-METHODS and probably a lot more of such
 ;;; preconditions.  That looks hairy and is probably not worth it,
 ;;; because this check will never be fast.
-(defun %check-cnm-args (cnm-args orig-args method-name-declaration)
+(defun %check-cnm-args (cnm-args orig-args method-cell)
   (when cnm-args
-    (let* ((gf (fdefinition (caar method-name-declaration)))
+    (let* ((gf (method-generic-function (car method-cell)))
            (omethods (compute-applicable-methods gf orig-args))
            (nmethods (compute-applicable-methods gf cnm-args)))
       (unless (equal omethods nmethods)
@@ -1452,13 +1526,16 @@ bootstrapping.
             new-value)
       (setf (getf (object-plist method) key default) new-value)))
 \f
-(defun load-defmethod
-    (class name quals specls ll initargs source-location)
-  (setq initargs (copy-tree initargs))
-  (setf (getf (getf initargs 'plist) :name)
-        (make-method-spec name quals specls))
-  (load-defmethod-internal class name quals specls
-                           ll initargs source-location))
+(defun load-defmethod (class name quals specls ll initargs source-location)
+  (let ((method-cell (getf initargs 'method-cell)))
+    (setq initargs (copy-tree initargs))
+    (when method-cell
+      (setf (getf initargs 'method-cell) method-cell))
+    #+nil
+    (setf (getf (getf initargs 'plist) :name)
+          (make-method-spec name quals specls))
+    (load-defmethod-internal class name quals specls
+                             ll initargs source-location)))
 
 (defun load-defmethod-internal
     (method-class gf-spec qualifiers specializers lambda-list
@@ -1468,10 +1545,7 @@ bootstrapping.
     (let* ((gf (fdefinition gf-spec))
            (method (and (generic-function-p gf)
                         (generic-function-methods gf)
-                        (find-method gf
-                                     qualifiers
-                                     (parse-specializers specializers)
-                                     nil))))
+                        (find-method gf qualifiers specializers nil))))
       (when method
         (style-warn "redefining ~S~{ ~S~} ~S in DEFMETHOD"
                     gf-spec qualifiers specializers))))
@@ -1492,15 +1566,20 @@ bootstrapping.
               method-class (class-name (class-of method))))
     method))
 
-(defun make-method-spec (gf-spec qualifiers unparsed-specializers)
-  `(slow-method ,gf-spec ,@qualifiers ,unparsed-specializers))
+(defun make-method-spec (gf qualifiers specializers)
+  (let ((name (generic-function-name gf))
+        (unparsed-specializers (unparse-specializers gf specializers)))
+    `(slow-method ,name ,@qualifiers ,unparsed-specializers)))
 
 (defun initialize-method-function (initargs method)
   (let* ((mf (getf initargs :function))
          (mff (and (typep mf '%method-function)
                    (%method-function-fast-function mf)))
          (plist (getf initargs 'plist))
-         (name (getf plist :name)))
+         (name (getf plist :name))
+         (method-cell (getf initargs 'method-cell)))
+    (when method-cell
+      (setf (car method-cell) method))
     (when name
       (when mf
         (setq mf (set-fun-name mf name)))
@@ -1954,11 +2033,10 @@ bootstrapping.
          (if existing
              (make-early-gf spec lambda-list lambda-list-p existing
                             argument-precedence-order source-location)
-             (error "The function ~S is not already defined." spec)))
+             (bug "The function ~S is not already defined." spec)))
         (existing
-         (error "~S should be on the list ~S."
-                spec
-                '*!generic-function-fixups*))
+         (bug "~S should be on the list ~S."
+              spec '*!generic-function-fixups*))
         (t
          (pushnew spec *!early-generic-functions* :test #'equal)
          (make-early-gf spec lambda-list lambda-list-p nil
@@ -2203,7 +2281,6 @@ bootstrapping.
 (defun real-make-a-method
        (class qualifiers lambda-list specializers initargs doc
         &rest args &key slot-name object-class method-class-function)
-  (setq specializers (parse-specializers specializers))
   (if method-class-function
       (let* ((object-class (if (classp object-class) object-class
                                (find-class object-class)))
@@ -2276,11 +2353,8 @@ bootstrapping.
 (defun (setf early-method-initargs) (new-value early-method)
   (setf (fifth (fifth early-method)) new-value))
 
-(defun early-add-named-method (generic-function-name
-                               qualifiers
-                               specializers
-                               arglist
-                               &rest initargs)
+(defun early-add-named-method (generic-function-name qualifiers
+                               specializers arglist &rest initargs)
   (let* (;; we don't need to deal with the :generic-function-class
          ;; argument here because the default,
          ;; STANDARD-GENERIC-FUNCTION, is right for all early generic
@@ -2290,15 +2364,13 @@ bootstrapping.
            (dolist (m (early-gf-methods gf))
              (when (and (equal (early-method-specializers m) specializers)
                         (equal (early-method-qualifiers m) qualifiers))
-               (return m))))
-         (new (make-a-method 'standard-method
-                             qualifiers
-                             arglist
-                             specializers
-                             initargs
-                             ())))
-    (when existing (remove-method gf existing))
-    (add-method gf new)))
+               (return m)))))
+    (setf (getf (getf initargs 'plist) :name)
+          (make-method-spec gf qualifiers specializers))
+    (let ((new (make-a-method 'standard-method qualifiers arglist
+                              specializers initargs ())))
+      (when existing (remove-method gf existing))
+      (add-method gf new))))
 
 ;;; This is the early version of ADD-METHOD. Later this will become a
 ;;; generic function. See !FIX-EARLY-GENERIC-FUNCTIONS which has
@@ -2405,7 +2477,7 @@ bootstrapping.
              (gf (gdefinition fspec))
              (methods (mapcar (lambda (method)
                                 (let* ((lambda-list (first method))
-                                       (specializers (second method))
+                                       (specializers (mapcar #'find-class (second method)))
                                        (method-fn-name (third method))
                                        (fn-name (or method-fn-name fspec))
                                        (fn (fdefinition fn-name))
@@ -2445,63 +2517,17 @@ bootstrapping.
     (setq spec-ll (pop cdr-of-form))
     (values name qualifiers spec-ll cdr-of-form)))
 
-(defun parse-specializers (specializers)
+(defun parse-specializers (generic-function specializers)
   (declare (list specializers))
   (flet ((parse (spec)
-           (let ((result (specializer-from-type spec)))
-             (if (specializerp result)
-                 result
-                 (if (symbolp spec)
-                     (error "~S was used as a specializer,~%~
-                             but is not the name of a class."
-                            spec)
-                     (error "~S is not a legal specializer." spec))))))
+           (parse-specializer-using-class generic-function spec)))
     (mapcar #'parse specializers)))
 
-(defun unparse-specializers (specializers-or-method)
-  (if (listp specializers-or-method)
-      (flet ((unparse (spec)
-               (if (specializerp spec)
-                   (let ((type (specializer-type spec)))
-                     (if (and (consp type)
-                              (eq (car type) 'class))
-                         (let* ((class (cadr type))
-                                (class-name (class-name class)))
-                           (if (eq class (find-class class-name nil))
-                               class-name
-                               type))
-                         type))
-                   (error "~S is not a legal specializer." spec))))
-        (mapcar #'unparse specializers-or-method))
-      (unparse-specializers (method-specializers specializers-or-method))))
-
-(defun parse-method-or-spec (spec &optional (errorp t))
-  (let (gf method name temp)
-    (if (method-p spec)
-        (setq method spec
-              gf (method-generic-function method)
-              temp (and gf (generic-function-name gf))
-              name (if temp
-                       (make-method-spec temp
-                                         (method-qualifiers method)
-                                         (unparse-specializers
-                                          (method-specializers method)))
-                       (make-symbol (format nil "~S" method))))
-        (multiple-value-bind (gf-spec quals specls)
-            (parse-defmethod spec)
-          (and (setq gf (and (or errorp (fboundp gf-spec))
-                             (gdefinition gf-spec)))
-               (let ((nreq (compute-discriminating-function-arglist-info gf)))
-                 (setq specls (append (parse-specializers specls)
-                                      (make-list (- nreq (length specls))
-                                                 :initial-element
-                                                 *the-class-t*)))
-                 (and
-                   (setq method (get-method gf quals specls errorp))
-                   (setq name
-                         (make-method-spec
-                          gf-spec quals (unparse-specializers specls))))))))
-    (values gf method name)))
+(defun unparse-specializers (generic-function specializers)
+  (declare (list specializers))
+  (flet ((unparse (spec)
+           (unparse-specializer-using-class generic-function spec)))
+    (mapcar #'unparse specializers)))
 \f
 (defun extract-parameters (specialized-lambda-list)
   (multiple-value-bind (parameters ignore1 ignore2)
index 59a84b1..3bf72db 100644 (file)
 
 ;;; interface
 (defun specializer-from-type (type &aux args)
+  (when (symbolp type)
+    (return-from specializer-from-type (find-class type)))
   (when (consp type)
     (setq args (cdr type) type (car type)))
   (cond ((symbolp type)
-         (or (and (null args) (find-class type))
-             (ecase type
+         (or (ecase type
                (class    (coerce-to-class (car args)))
                (prototype (make-instance 'class-prototype-specializer
                                          :object (coerce-to-class (car args))))
 (defclass method (metaobject) ())
 
 (defclass standard-method (plist-mixin definition-source-mixin method)
-  ((%generic-function
-    :initform nil
-    :accessor method-generic-function)
-   (qualifiers
-    :initform ()
-    :initarg  :qualifiers
-    :reader method-qualifiers)
-   (specializers
-    :initform ()
-    :initarg  :specializers
-    :reader method-specializers)
-   (lambda-list
-    :initform ()
-    :initarg  :lambda-list
-    :reader method-lambda-list)
+  ((%generic-function :initform nil :accessor method-generic-function)
+   (qualifiers :initform () :initarg :qualifiers :reader method-qualifiers)
+   (specializers :initform () :initarg :specializers
+                 :reader method-specializers)
+   (lambda-list :initform () :initarg :lambda-list :reader method-lambda-list)
    (%function :initform nil :initarg :function :reader method-function)
    (%documentation :initform nil :initarg :documentation)))
 
index 23b619b..c727c43 100644 (file)
             (format stream "~&  (~A ~{~S ~}~:S)~%"
                     gf-name
                     (method-qualifiers method)
-                    (unparse-specializers method))
+                    (unparse-specializers fun (method-specializers method)))
             (when (documentation method t)
               (format stream "~&    Method documentation: ~A"
                       (documentation method t))))))))
index 0a63836..f1cf98f 100644 (file)
 ;;; a "contrib" directory eventually?
 
 #|
+(defun parse-method-or-spec (spec &optional (errorp t))
+  (let (gf method name temp)
+    (if (method-p spec)
+        (setq method spec
+              gf (method-generic-function method)
+              temp (and gf (generic-function-name gf))
+              name (if temp
+                       (make-method-spec temp
+                                         (method-qualifiers method)
+                                         (unparse-specializers
+                                          (method-specializers method)))
+                       (make-symbol (format nil "~S" method))))
+        (multiple-value-bind (gf-spec quals specls)
+            (parse-defmethod spec)
+          (and (setq gf (and (or errorp (fboundp gf-spec))
+                             (gdefinition gf-spec)))
+               (let ((nreq (compute-discriminating-function-arglist-info gf)))
+                 (setq specls (append (parse-specializers specls)
+                                      (make-list (- nreq (length specls))
+                                                 :initial-element
+                                                 *the-class-t*)))
+                 (and
+                   (setq method (get-method gf quals specls errorp))
+                   (setq name
+                         (make-method-spec
+                          gf-spec quals (unparse-specializers specls))))))))
+    (values gf method name)))
+
 ;;; TRACE-METHOD and UNTRACE-METHOD accept method specs as arguments. A
 ;;; method-spec should be a list like:
 ;;;   (<generic-function-spec> qualifiers* (specializers*))
   (fdefinition name))
 |#
 \f
+#|
 ;;;; Helper for slightly newer trace implementation, based on
 ;;;; breakpoint stuff.  The above is potentially still useful, so it's
 ;;;; left in, commented.
+
+;;; (this turned out to be a roundabout way of doing things)
 (defun list-all-maybe-method-names (gf)
   (let (result)
     (dolist (method (generic-function-methods gf) (nreverse result))
       (let ((spec (nth-value 2 (parse-method-or-spec method))))
         (push spec result)
         (push (list* 'fast-method (cdr spec)) result)))))
+|#
 \f
 ;;;; MAKE-LOAD-FORM
 
index af0239f..83e9721 100644 (file)
@@ -24,6 +24,7 @@
 (in-package "SB-PCL")
 
 (!fix-early-generic-functions)
+(!fix-ensure-accessor-specializers)
 (compute-standard-slot-locations)
 (dolist (s '(condition structure-object))
   (dohash (k v (classoid-subclasses (find-classoid s)))
index e3379e2..547557a 100644 (file)
 
 (defgeneric map-dependents (metaobject function))
 
+(defgeneric parse-specializer-using-class (generic-function specializer-name))
+
 (defgeneric remove-boundp-method (class generic-function))
 
 (defgeneric remove-dependent (metaobject dependent))
 ;;; This controls DESCRIBE-OBJECT (SLOT-OBJECT STREAM) behavior.
 (defgeneric slots-to-inspect (class object))
 
+(defgeneric unparse-specializer-using-class (generic-function specializer))
+
 (defgeneric update-gf-dfun (class gf))
 
 (defgeneric validate-superclass (class superclass))
 
 (defgeneric add-writer-method (class generic-function slot-name slot-documentation))
 
-(defgeneric make-method-lambda (proto-generic-function
-                                proto-method
-                                lambda-expression
-                                environment))
+(defgeneric make-method-lambda
+    (proto-generic-function proto-method lambda-expression environment))
+
+(defgeneric make-method-specializers-form
+    (proto-generic-function proto-method specializer-names environment))
 
 (defgeneric (setf slot-value-using-class) (new-value class object slotd))
 \f
 ;;;; 5 arguments
 
-(defgeneric make-method-initargs-form (proto-generic-function
-                                       proto-method
-                                       lambda-expression
-                                       lambda-list
-                                       environment))
+(defgeneric make-method-initargs-form
+    (proto-generic-function proto-method lambda-expression lambda-list
+     environment))
 \f
 ;;;; optional arguments
 
index 9bf5e04..954619a 100644 (file)
     (check-slot-name method slot-name)))
 
 (defmethod shared-initialize :after ((method standard-method) slot-names
-                                     &rest initargs &key)
-  (declare (ignore slot-names))
+                                     &rest initargs &key ((method-cell method-cell)))
+  (declare (ignore slot-names method-cell))
   (initialize-method-function initargs method))
-
 \f
 (defvar *the-class-generic-function*
   (find-class 'generic-function))
       (errorp (error "No generic function named ~S." name))
       (t nil))))
 
-(defun real-add-named-method (generic-function-name
-                              qualifiers
-                              specializers
-                              lambda-list
-                              &rest other-initargs)
+(defun real-add-named-method (generic-function-name qualifiers
+                              specializers lambda-list &rest other-initargs)
   (unless (and (fboundp generic-function-name)
                (typep (fdefinition generic-function-name) 'generic-function))
     (style-warn "implicitly creating new generic function ~S"
                generic-function-name
                :generic-function-class (class-of existing-gf))
               (ensure-generic-function generic-function-name)))
-         (specs (parse-specializers specializers))
-         (proto (method-prototype-for-gf generic-function-name))
-         (new (apply #'make-instance (class-of proto)
-                                     :qualifiers qualifiers
-                                     :specializers specs
-                                     :lambda-list lambda-list
-                                     other-initargs)))
-    (add-method generic-function new)
-    new))
+         (proto (method-prototype-for-gf generic-function-name)))
+    (setf (getf (getf other-initargs 'plist) :name)
+          (make-method-spec generic-function qualifiers specializers))
+    (let ((new (apply #'make-instance (class-of proto)
+                      :qualifiers qualifiers :specializers specializers
+                      :lambda-list lambda-list other-initargs)))
+      (add-method generic-function new)
+      new)))
 
 (define-condition find-method-length-mismatch
     (reference-condition simple-error)
   ;; function, or an error is signaled."
   ;;
   ;; This error checking is done by REAL-GET-METHOD.
-  (real-get-method generic-function
-                   qualifiers
-                   (parse-specializers specializers)
-                   errorp
-                   t))
+  (real-get-method
+   generic-function qualifiers
+   ;; ANSI for FIND-METHOD seems to imply that in fact specializers
+   ;; should always be passed in parsed form instead of being parsed
+   ;; at this point.  Since there's no ANSI-blessed way of getting an
+   ;; EQL specializer, that seems unnecessarily painful, so we are
+   ;; nice to our users.  -- CSR, 2007-06-01
+   (parse-specializers generic-function specializers) errorp t))
 \f
 ;;; Compute various information about a generic-function's arglist by looking
 ;;; at the argument lists of the methods. The hair for trying not to use
index 82966e3..b377444 100644 (file)
@@ -73,7 +73,9 @@
                   (and generic-function
                        (generic-function-name generic-function))
                   (method-qualifiers method)
-                  (unparse-specializers method)))
+                  (if generic-function
+                      (unparse-specializers generic-function (method-specializers method))
+                      (method-specializers method))))
         ;; FIXME: Why do we do CALL-NEXT-METHOD in this method (and
         ;; in the PRINT-OBJECT STANDARD-ACCESSOR-METHOD method too)?
         (call-next-method))))
@@ -86,7 +88,9 @@
                   (and generic-function
                        (generic-function-name generic-function))
                   (accessor-method-slot-name method)
-                  (unparse-specializers method)))
+                  (if generic-function
+                      (unparse-specializers generic-function (method-specializers method))
+                      (method-specializers method))))
         (call-next-method))))
 
 (defmethod print-object ((mc standard-method-combination) stream)
index e5e9d95..8abdb89 100644 (file)
 
 (in-package "SB-PCL")
 \f
-(defun ensure-accessor (type fun-name slot-name)
-  (unless (fboundp fun-name)
-    (multiple-value-bind (lambda-list specializers method-class initargs doc)
-        (ecase type
-          ;; FIXME: change SLOT-OBJECT here to T to get SLOT-MISSING
-          ;; behaviour for non-slot-objects too?
-          (reader
-           (values '(object) '(slot-object) 'global-reader-method
-                   (make-std-reader-method-function 'slot-object slot-name)
-                   "automatically-generated reader method"))
-          (writer
-           (values '(new-value object) '(t slot-object) 'global-writer-method
-                   (make-std-writer-method-function 'slot-object slot-name)
-                   "automatically-generated writer method"))
-          (boundp
-           (values '(object) '(slot-object) 'global-boundp-method
-                   (make-std-boundp-method-function 'slot-object slot-name)
-                   "automatically-generated boundp method")))
-      (let ((gf (ensure-generic-function fun-name :lambda-list lambda-list)))
-        (add-method gf (make-a-method method-class
-                                      () lambda-list specializers
-                                      initargs doc :slot-name slot-name)))))
-  t)
+(let ((reader-specializers '(slot-object))
+      (writer-specializers '(t slot-object)))
+  (defun ensure-accessor (type fun-name slot-name)
+    (unless (fboundp fun-name)
+      (multiple-value-bind (lambda-list specializers method-class initargs doc)
+          (ecase type
+            ;; FIXME: change SLOT-OBJECT here to T to get SLOT-MISSING
+            ;; behaviour for non-slot-objects too?
+            (reader
+             (values '(object) reader-specializers 'global-reader-method
+                     (make-std-reader-method-function 'slot-object slot-name)
+                     "automatically-generated reader method"))
+            (writer
+             (values '(new-value object) writer-specializers
+                     'global-writer-method
+                     (make-std-writer-method-function 'slot-object slot-name)
+                     "automatically-generated writer method"))
+            (boundp
+             (values '(object) reader-specializers 'global-boundp-method
+                     (make-std-boundp-method-function 'slot-object slot-name)
+                     "automatically-generated boundp method")))
+        (let ((gf (ensure-generic-function fun-name :lambda-list lambda-list)))
+          (add-method gf (make-a-method method-class
+                                        () lambda-list specializers
+                                        initargs doc :slot-name slot-name)))))
+    t)
+  ;; KLUDGE: this is maybe PCL bootstrap mechanism #6 or #7, invented
+  ;; by CSR in June 2007.  Making the bootstrap sane is getting higher
+  ;; on the "TODO: URGENT" list.
+  (defun !fix-ensure-accessor-specializers ()
+    (setf reader-specializers (mapcar #'find-class reader-specializers))
+    (setf writer-specializers (mapcar #'find-class writer-specializers))))
 
 (defmacro accessor-slot-value (object slot-name)
   (aver (constantp slot-name))
index dc00ae4..c839585 100644 (file)
@@ -88,8 +88,8 @@
     (assert (= 3 (b-of *foo*)))
     (assert (raises-error? (c-of *foo*)))))
 
-;; test that :documentation argument to slot specifiers are used as
-;; the docstrings of accessor methods.
+;;; test that :documentation argument to slot specifiers are used as
+;;; the docstrings of accessor methods.
 (defclass foo ()
   ((a :reader a-of :documentation "docstring for A")
    (b :writer set-b-of :documentation "docstring for B")
 
 (flet ((doc (fun)
          (documentation fun t)))
-  (assert (string= (doc (find-method #'a-of nil '((foo)))) "docstring for A"))
-  (assert (string= (doc (find-method #'set-b-of nil '(t (foo)))) "docstring for B"))
-  (assert (string= (doc (find-method #'c nil '((foo)))) "docstring for C"))
-  (assert (string= (doc (find-method #'(setf c) nil '(t (foo)))) "docstring for C")))
+  (assert (string= (doc (find-method #'a-of nil '(foo))) "docstring for A"))
+  (assert (string= (doc (find-method #'set-b-of nil '(t foo))) "docstring for B"))
+  (assert (string= (doc (find-method #'c nil '(foo))) "docstring for C"))
+  (assert (string= (doc (find-method #'(setf c) nil '(t foo))) "docstring for C")))
+\f
+;;; some nasty tests of NO-NEXT-METHOD.
+(defvar *method-with-no-next-method*)
+(defvar *nnm-count* 0)
+(defun make-nnm-tester (x)
+  (setq *method-with-no-next-method* (defmethod nnm-tester ((y (eql x))) (call-next-method))))
+(make-nnm-tester 1)
+(defmethod no-next-method ((gf (eql #'nnm-tester)) method &rest args)
+  (assert (eql method *method-with-no-next-method*))
+  (incf *nnm-count*))
+(with-test (:name (no-next-method :unknown-specializer))
+  (nnm-tester 1)
+  (assert (= *nnm-count* 1)))
+(let ((gf #'nnm-tester))
+  (reinitialize-instance gf :name 'new-nnm-tester)
+  (setf (fdefinition 'new-nnm-tester) gf))
+(with-test (:name (no-next-method :gf-name-changed))
+  (new-nnm-tester 1)
+  (assert (= *nnm-count* 2)))
index 2ccde91..b43fe4a 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.6.11"
+"1.0.6.12"