X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fpcl%2Fboot.lisp;h=bedfc51210697a6e391b5247d9a398438dca3aaf;hb=28b2447f2775779fe49fd024d8cce069060431c6;hp=8d87432ec6ef63144c9ded2457ba45cc3003bff0;hpb=562e48a2bd3467121e24214110e535c841fbb622;p=sbcl.git diff --git a/src/pcl/boot.lisp b/src/pcl/boot.lisp index 8d87432..bedfc51 100644 --- a/src/pcl/boot.lisp +++ b/src/pcl/boot.lisp @@ -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 @@ -242,8 +252,9 @@ bootstrapping. (defun load-defgeneric (fun-name lambda-list source-location &rest initargs) (when (fboundp fun-name) - (style-warn "redefining ~S in DEFGENERIC" fun-name) (let ((fun (fdefinition fun-name))) + (warn 'sb-kernel:redefinition-with-defgeneric :name fun-name + :old fun :new-location source-location) (when (generic-function-p fun) (loop for method in (generic-function-initial-methods fun) do (remove-method fun method)) @@ -298,18 +309,48 @@ bootstrapping. ;; belong here! (aver (not morep))))) -(defmacro defmethod (&rest args &environment env) +(defmacro defmethod (&rest args) (multiple-value-bind (name qualifiers lambda-list body) (parse-defmethod args) - (multiple-value-bind (proto-gf proto-method) - (prototypes-for-make-method-lambda name) - (expand-defmethod name - proto-gf - proto-method - qualifiers - lambda-list - body - env)))) + `(progn + ;; KLUDGE: this double expansion is quite a monumental + ;; workaround: it comes about because of a fantastic interaction + ;; between the processing rules of CLHS 3.2.3.1 and the + ;; bizarreness of MAKE-METHOD-LAMBDA. + ;; + ;; MAKE-METHOD-LAMBDA can be called by the user, and if the + ;; lambda itself doesn't refer to outside bindings the return + ;; value must be compileable in the null lexical environment. + ;; However, the function must also refer somehow to the + ;; associated method object, so that it can call NO-NEXT-METHOD + ;; with the appropriate arguments if there is no next method -- + ;; but when the function is generated, the method object doesn't + ;; exist yet. + ;; + ;; In order to resolve this issue, we insert a literal cons cell + ;; into the body of the method lambda, return the same cons cell + ;; as part of the second (initargs) return value of + ;; MAKE-METHOD-LAMBDA, and a method on INITIALIZE-INSTANCE fills + ;; in the cell when the method is created. However, this + ;; strategy depends on having a fresh cons cell for every method + ;; lambda, which (without the workaround below) is skewered by + ;; the processing in CLHS 3.2.3.1, which permits implementations + ;; to macroexpand the bodies of EVAL-WHEN forms with both + ;; :COMPILE-TOPLEVEL and :LOAD-TOPLEVEL only once. The + ;; expansion below forces the double expansion in those cases, + ;; while expanding only once in the common case. + (eval-when (:load-toplevel) + (%defmethod-expander ,name ,qualifiers ,lambda-list ,body)) + (eval-when (:execute) + (%defmethod-expander ,name ,qualifiers ,lambda-list ,body))))) + +(defmacro %defmethod-expander + (name qualifiers lambda-list body &environment env) + (multiple-value-bind (proto-gf proto-method) + (prototypes-for-make-method-lambda name) + (expand-defmethod name proto-gf proto-method qualifiers + lambda-list body env))) + (defun prototypes-for-make-method-lambda (name) (if (not (eq *boot-state* 'complete)) @@ -358,11 +399,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 +412,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 +424,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 +458,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 +489,8 @@ bootstrapping. ,@(cdddr initargs-form))))) (make-defmethod-form-internal name qualifiers + specializers + #+nil `(list ,@(mapcar (lambda (specializer) (if (consp specializer) ``(,',(car specializer) @@ -460,9 +514,6 @@ bootstrapping. (sb-c:source-location))) (defmacro make-method-function (method-lambda &environment env) - (make-method-function-internal method-lambda env)) - -(defun make-method-function-internal (method-lambda &optional env) (multiple-value-bind (proto-gf proto-method) (prototypes-for-make-method-lambda nil) (multiple-value-bind (method-function-lambda initargs) @@ -525,143 +576,22 @@ bootstrapping. (setf (gdefinition 'make-method-initargs-form) (symbol-function 'real-make-method-initargs-form))) +;;; When bootstrapping PCL MAKE-METHOD-LAMBDA starts out as a regular +;;; functions: REAL-MAKE-METHOD-LAMBDA set to the fdefinition of +;;; MAKE-METHOD-LAMBDA. Once generic functions are born, the +;;; REAL-MAKE-METHOD lambda is used as the body of the default method. +;;; MAKE-METHOD-LAMBDA-INTERNAL is split out into a separate function +;;; so that changing it in a live image is easy, and changes actually +;;; take effect. (defun real-make-method-lambda (proto-gf proto-method method-lambda env) - (declare (ignore proto-gf proto-method)) - (make-method-lambda-internal method-lambda env)) + (make-method-lambda-internal proto-gf proto-method method-lambda env)) -;;; a helper function for creating Python-friendly type declarations -;;; in DEFMETHOD forms -(defun parameter-specializer-declaration-in-defmethod (parameter specializer) - (cond ((and (consp specializer) - (eq (car specializer) 'eql)) - ;; KLUDGE: ANSI, in its wisdom, says that - ;; EQL-SPECIALIZER-FORMs in EQL specializers are evaluated at - ;; DEFMETHOD expansion time. Thus, although one might think - ;; that in - ;; (DEFMETHOD FOO ((X PACKAGE) - ;; (Y (EQL 12)) - ;; ..)) - ;; the PACKAGE and (EQL 12) forms are both parallel type - ;; names, they're not, as is made clear when you do - ;; (DEFMETHOD FOO ((X PACKAGE) - ;; (Y (EQL 'BAR))) - ;; ..) - ;; where Y needs to be a symbol named "BAR", not some cons - ;; made by (CONS 'QUOTE 'BAR). I.e. when the - ;; EQL-SPECIALIZER-FORM is (EQL 'X), it requires an argument - ;; to be of type (EQL X). It'd be easy to transform one to - ;; the other, but it'd be somewhat messier to do so while - ;; ensuring that the EQL-SPECIALIZER-FORM is only EVAL'd - ;; once. (The new code wouldn't be messy, but it'd require a - ;; big transformation of the old code.) So instead we punt. - ;; -- WHN 20000610 - '(ignorable)) - ((member specializer - ;; KLUDGE: For some low-level implementation - ;; classes, perhaps because of some problems related - ;; to the incomplete integration of PCL into SBCL's - ;; type system, some specializer classes can't be - ;; declared as argument types. E.g. - ;; (DEFMETHOD FOO ((X SLOT-OBJECT)) - ;; (DECLARE (TYPE SLOT-OBJECT X)) - ;; ..) - ;; loses when - ;; (DEFSTRUCT BAR A B) - ;; (FOO (MAKE-BAR)) - ;; perhaps because of the way that STRUCTURE-OBJECT - ;; inherits both from SLOT-OBJECT and from - ;; SB-KERNEL:INSTANCE. In an effort to sweep such - ;; problems under the rug, we exclude these problem - ;; cases by blacklisting them here. -- WHN 2001-01-19 - (list 'slot-object #+nil (find-class 'slot-object))) - '(ignorable)) - ((not (eq *boot-state* 'complete)) - ;; KLUDGE: PCL, in its wisdom, sometimes calls methods with - ;; types which don't match their specializers. (Specifically, - ;; it calls ENSURE-CLASS-USING-CLASS (T NULL) with a non-NULL - ;; second argument.) Hopefully it only does this kind of - ;; weirdness when bootstrapping.. -- WHN 20000610 - '(ignorable)) - ((typep specializer 'eql-specializer) - `(type (eql ,(eql-specializer-object specializer)) ,parameter)) - ((var-globally-special-p parameter) - ;; KLUDGE: Don't declare types for global special variables - ;; -- our rebinding magic for SETQ cases don't work right - ;; there. - ;; - ;; FIXME: It would be better to detect the SETQ earlier and - ;; skip declarations for specials only when needed, not - ;; always. - ;; - ;; --NS 2004-10-14 - '(ignorable)) - (t - ;; Otherwise, we can usually make Python very happy. - ;; - ;; KLUDGE: Since INFO doesn't work right for class objects here, - ;; and they are valid specializers, see if the specializer is - ;; a named class, and use the name in that case -- otherwise - ;; the class instance is ok, since info will just return NIL, NIL. - ;; - ;; We still need to deal with the class case too, but at - ;; least #.(find-class 'integer) and integer as equivalent - ;; specializers with this. - (let* ((specializer-nameoid - (if (and (typep specializer 'class) - (let ((name (class-name specializer))) - (and name (symbolp name) - (eq specializer (find-class name nil))))) - (class-name specializer) - specializer)) - (kind (info :type :kind specializer-nameoid))) - - (flet ((specializer-nameoid-class () - (typecase specializer-nameoid - (symbol (find-class specializer-nameoid nil)) - (class specializer-nameoid) - (class-eq-specializer - (specializer-class specializer-nameoid)) - (t nil)))) - (ecase kind - ((:primitive) `(type ,specializer-nameoid ,parameter)) - ((:defined) - (let ((class (specializer-nameoid-class))) - ;; CLASS can be null here if the user has - ;; erroneously tried to use a defined type as a - ;; specializer; it can be a non-BUILT-IN-CLASS if - ;; the user defines a type and calls (SETF - ;; FIND-CLASS) in a consistent way. - (when (and class (typep class 'built-in-class)) - `(type ,specializer-nameoid ,parameter)))) - ((:instance nil) - (let ((class (specializer-nameoid-class))) - (cond - (class - (if (typep class '(or built-in-class structure-class)) - `(type ,class ,parameter) - ;; don't declare CLOS classes as parameters; - ;; it's too expensive. - '(ignorable))) - (t - ;; we can get here, and still not have a failure - ;; case, by doing MOP programming like (PROGN - ;; (ENSURE-CLASS 'FOO) (DEFMETHOD BAR ((X FOO)) - ;; ...)). Best to let the user know we haven't - ;; been able to extract enough information: - (style-warn - "~@" - specializer-nameoid - 'parameter-specializer-declaration-in-defmethod) - '(ignorable))))) - ((:forthcoming-defclass-type) - '(ignorable)))))))) - -;;; For passing a list (groveled by the walker) of the required -;;; parameters whose bindings are modified in the method body to the -;;; optimized-slot-value* macros. -(define-symbol-macro %parameter-binding-modified ()) +(unless (fboundp 'make-method-lambda) + (setf (gdefinition 'make-method-lambda) + (symbol-function 'real-make-method-lambda))) -(defun make-method-lambda-internal (method-lambda &optional env) +(defun make-method-lambda-internal (proto-gf proto-method method-lambda env) + (declare (ignore proto-gf proto-method)) (unless (and (consp method-lambda) (eq (car method-lambda) 'lambda)) (error "The METHOD-LAMBDA argument to MAKE-METHOD-LAMBDA, ~S, ~ is not a lambda form." @@ -672,7 +602,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 @@ -680,7 +618,6 @@ bootstrapping. parameters specializers)) (slots (mapcar #'list required-parameters)) - (calls (list nil)) (class-declarations `(declare ;; These declarations seem to be used by PCL to pass @@ -755,29 +692,24 @@ bootstrapping. (walk-method-lambda method-lambda required-parameters env - slots - calls) + slots) (multiple-value-bind (walked-lambda-body walked-declarations walked-documentation) (parse-body (cddr walked-lambda)) (declare (ignore walked-documentation)) (when (some #'cdr slots) - (multiple-value-bind (slot-name-lists call-list) - (slot-name-lists-from-slots slots calls) + (let ((slot-name-lists (slot-name-lists-from-slots slots))) (setq plist `(,@(when slot-name-lists `(:slot-name-lists ,slot-name-lists)) - ,@(when call-list - `(:call-list ,call-list)) ,@plist)) (setq walked-lambda-body `((pv-binding (,required-parameters ,slot-name-lists (load-time-value (intern-pv-table - :slot-name-lists ',slot-name-lists - :call-list ',call-list))) + :slot-name-lists ',slot-name-lists))) ,@walked-lambda-body))))) (when (and (memq '&key lambda-list) (not (memq '&allow-other-keys lambda-list))) @@ -792,14 +724,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 +736,199 @@ bootstrapping. (declare (enable-package-locks %parameter-binding-modified)) ,@walked-lambda-body)))) - `(,@(when plist - `(plist ,plist)) - ,@(when documentation - `(:documentation ,documentation))))))))))) + `(,@(when call-next-method-p `(method-cell ,method-cell)) + ,@(when plist `(plist ,plist)) + ,@(when documentation `(:documentation ,documentation))))))))))) + +(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))) -(unless (fboundp 'make-method-lambda) - (setf (gdefinition 'make-method-lambda) - (symbol-function 'real-make-method-lambda))) +;;; a helper function for creating Python-friendly type declarations +;;; in DEFMETHOD forms +(defun parameter-specializer-declaration-in-defmethod (parameter specializer) + (cond ((and (consp specializer) + (eq (car specializer) 'eql)) + ;; KLUDGE: ANSI, in its wisdom, says that + ;; EQL-SPECIALIZER-FORMs in EQL specializers are evaluated at + ;; DEFMETHOD expansion time. Thus, although one might think + ;; that in + ;; (DEFMETHOD FOO ((X PACKAGE) + ;; (Y (EQL 12)) + ;; ..)) + ;; the PACKAGE and (EQL 12) forms are both parallel type + ;; names, they're not, as is made clear when you do + ;; (DEFMETHOD FOO ((X PACKAGE) + ;; (Y (EQL 'BAR))) + ;; ..) + ;; where Y needs to be a symbol named "BAR", not some cons + ;; made by (CONS 'QUOTE 'BAR). I.e. when the + ;; EQL-SPECIALIZER-FORM is (EQL 'X), it requires an argument + ;; to be of type (EQL X). It'd be easy to transform one to + ;; the other, but it'd be somewhat messier to do so while + ;; ensuring that the EQL-SPECIALIZER-FORM is only EVAL'd + ;; once. (The new code wouldn't be messy, but it'd require a + ;; big transformation of the old code.) So instead we punt. + ;; -- WHN 20000610 + '(ignorable)) + ((member specializer + ;; KLUDGE: For some low-level implementation + ;; classes, perhaps because of some problems related + ;; to the incomplete integration of PCL into SBCL's + ;; type system, some specializer classes can't be + ;; declared as argument types. E.g. + ;; (DEFMETHOD FOO ((X SLOT-OBJECT)) + ;; (DECLARE (TYPE SLOT-OBJECT X)) + ;; ..) + ;; loses when + ;; (DEFSTRUCT BAR A B) + ;; (FOO (MAKE-BAR)) + ;; perhaps because of the way that STRUCTURE-OBJECT + ;; inherits both from SLOT-OBJECT and from + ;; SB-KERNEL:INSTANCE. In an effort to sweep such + ;; problems under the rug, we exclude these problem + ;; cases by blacklisting them here. -- WHN 2001-01-19 + (list 'slot-object #+nil (find-class 'slot-object))) + '(ignorable)) + ((not (eq *boot-state* 'complete)) + ;; KLUDGE: PCL, in its wisdom, sometimes calls methods with + ;; types which don't match their specializers. (Specifically, + ;; it calls ENSURE-CLASS-USING-CLASS (T NULL) with a non-NULL + ;; second argument.) Hopefully it only does this kind of + ;; weirdness when bootstrapping.. -- WHN 20000610 + '(ignorable)) + ((typep specializer 'eql-specializer) + `(type (eql ,(eql-specializer-object specializer)) ,parameter)) + ((var-globally-special-p parameter) + ;; KLUDGE: Don't declare types for global special variables + ;; -- our rebinding magic for SETQ cases don't work right + ;; there. + ;; + ;; FIXME: It would be better to detect the SETQ earlier and + ;; skip declarations for specials only when needed, not + ;; always. + ;; + ;; --NS 2004-10-14 + '(ignorable)) + (t + ;; Otherwise, we can usually make Python very happy. + ;; + ;; KLUDGE: Since INFO doesn't work right for class objects here, + ;; and they are valid specializers, see if the specializer is + ;; a named class, and use the name in that case -- otherwise + ;; the class instance is ok, since info will just return NIL, NIL. + ;; + ;; We still need to deal with the class case too, but at + ;; least #.(find-class 'integer) and integer as equivalent + ;; specializers with this. + (let* ((specializer-nameoid + (if (and (typep specializer 'class) + (let ((name (class-name specializer))) + (and name (symbolp name) + (eq specializer (find-class name nil))))) + (class-name specializer) + specializer)) + (kind (info :type :kind specializer-nameoid))) + + (flet ((specializer-nameoid-class () + (typecase specializer-nameoid + (symbol (find-class specializer-nameoid nil)) + (class specializer-nameoid) + (class-eq-specializer + (specializer-class specializer-nameoid)) + (t nil)))) + (ecase kind + ((:primitive) `(type ,specializer-nameoid ,parameter)) + ((:defined) + (let ((class (specializer-nameoid-class))) + ;; CLASS can be null here if the user has + ;; erroneously tried to use a defined type as a + ;; specializer; it can be a non-BUILT-IN-CLASS if + ;; the user defines a type and calls (SETF + ;; FIND-CLASS) in a consistent way. + (when (and class (typep class 'built-in-class)) + `(type ,specializer-nameoid ,parameter)))) + ((:instance nil) + (let ((class (specializer-nameoid-class))) + (cond + (class + (if (typep class '(or built-in-class structure-class)) + `(type ,class ,parameter) + ;; don't declare CLOS classes as parameters; + ;; it's too expensive. + '(ignorable))) + (t + ;; we can get here, and still not have a failure + ;; case, by doing MOP programming like (PROGN + ;; (ENSURE-CLASS 'FOO) (DEFMETHOD BAR ((X FOO)) + ;; ...)). Best to let the user know we haven't + ;; been able to extract enough information: + (style-warn + "~@" + specializer-nameoid + 'parameter-specializer-declaration-in-defmethod) + '(ignorable))))) + ((:forthcoming-defclass-type) + '(ignorable)))))))) + +;;; For passing a list (groveled by the walker) of the required +;;; parameters whose bindings are modified in the method body to the +;;; optimized-slot-value* macros. +(define-symbol-macro %parameter-binding-modified ()) (defmacro simple-lexical-method-functions ((lambda-list method-args @@ -844,7 +954,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 +969,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 +978,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) @@ -913,7 +1016,7 @@ bootstrapping. (defstruct (fast-method-call (:copier nil)) (function #'identity :type function) - pv-cell + pv next-method-call arg-info) (defstruct (constant-fast-method-call @@ -930,7 +1033,7 @@ bootstrapping. (defmacro invoke-fast-method-call (method-call restp &rest required-args+rest-arg) `(,(if restp 'apply 'funcall) (fast-method-call-function ,method-call) - (fast-method-call-pv-cell ,method-call) + (fast-method-call-pv ,method-call) (fast-method-call-next-method-call ,method-call) ,@required-args+rest-arg)) @@ -940,7 +1043,7 @@ bootstrapping. &rest required-args) (macrolet ((generate-call (n) ``(funcall (fast-method-call-function ,method-call) - (fast-method-call-pv-cell ,method-call) + (fast-method-call-pv ,method-call) (fast-method-call-next-method-call ,method-call) ,@required-args ,@(loop for x below ,n @@ -954,7 +1057,7 @@ bootstrapping. (0 ,(generate-call 0)) (1 ,(generate-call 1)) (t (multiple-value-call (fast-method-call-function ,method-call) - (values (fast-method-call-pv-cell ,method-call)) + (values (fast-method-call-pv ,method-call)) (values (fast-method-call-next-method-call ,method-call)) ,@required-args (sb-c::%more-arg-values ,more-context 0 ,more-count)))))) @@ -1102,7 +1205,7 @@ bootstrapping. (nreq (car arg-info))) (if restp (apply (fast-method-call-function emf) - (fast-method-call-pv-cell emf) + (fast-method-call-pv emf) (fast-method-call-next-method-call emf) args) (cond ((null args) @@ -1125,7 +1228,7 @@ bootstrapping. :format-arguments nil))) (t (apply (fast-method-call-function emf) - (fast-method-call-pv-cell emf) + (fast-method-call-pv emf) (fast-method-call-next-method-call emf) args)))))) (method-call @@ -1162,7 +1265,7 @@ bootstrapping. (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 +1280,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 +1289,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 +1307,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 +1337,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) @@ -1331,7 +1434,7 @@ bootstrapping. when (eq key keyword) return tail)) -(defun walk-method-lambda (method-lambda required-parameters env slots calls) +(defun walk-method-lambda (method-lambda required-parameters env slots) (let (;; flag indicating that CALL-NEXT-METHOD should be in the ;; method definition (call-next-method-p nil) @@ -1394,7 +1497,7 @@ bootstrapping. ;; another binding it won't have a %CLASS ;; declaration anymore, and this won't get ;; executed. - (pushnew var parameters-setqd)))) + (pushnew var parameters-setqd :test #'eq)))) form) ((and (eq (car form) 'function) (cond ((eq (cadr form) 'call-next-method) @@ -1408,15 +1511,12 @@ bootstrapping. (t nil)))) ((and (memq (car form) '(slot-value set-slot-value slot-boundp)) - (constantp (caddr form))) - (let ((parameter (can-optimize-access form - required-parameters - env))) - (let ((fun (ecase (car form) - (slot-value #'optimize-slot-value) - (set-slot-value #'optimize-set-slot-value) - (slot-boundp #'optimize-slot-boundp)))) - (funcall fun slots parameter form)))) + (constantp (caddr form) env)) + (let ((fun (ecase (car form) + (slot-value #'optimize-slot-value) + (set-slot-value #'optimize-set-slot-value) + (slot-boundp #'optimize-slot-boundp)))) + (funcall fun form slots required-parameters env))) (t form)))) (let ((walked-lambda (walk-form method-lambda env #'walk-function))) @@ -1452,13 +1552,16 @@ bootstrapping. new-value) (setf (getf (object-plist method) key default) new-value))) -(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,13 +1571,12 @@ 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)))) + (style-warn 'sb-kernel:redefinition-with-defmethod + :generic-function gf-spec :old-method method + :qualifiers qualifiers :specializers specializers + :new-location source-location)))) (let ((method (apply #'add-named-method gf-spec qualifiers specializers lambda-list :definition-source source-location @@ -1492,15 +1594,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))) @@ -1509,11 +1616,10 @@ bootstrapping. (set-fun-name mff fast-name)))) (when plist (let ((plist plist)) - (let ((snl (getf plist :slot-name-lists)) - (cl (getf plist :call-list))) - (when (or snl cl) + (let ((snl (getf plist :slot-name-lists))) + (when snl (setf (method-plist-value method :pv-table) - (intern-pv-table :slot-name-lists snl :call-list cl)))))))) + (intern-pv-table :slot-name-lists snl)))))))) (defun analyze-lambda-list (lambda-list) (flet (;; FIXME: Is this redundant with SB-C::MAKE-KEYWORD-FOR-ARG? @@ -1595,9 +1701,6 @@ bootstrapping. (when (or allow-other-keys-p old-allowp) '(&allow-other-keys))))) *)))) - -(defun defgeneric-declaration (spec lambda-list) - `(ftype ,(ftype-declaration-from-lambda-list lambda-list spec) ,spec)) ;;;; early generic function support @@ -1810,14 +1913,19 @@ bootstrapping. (aver (= (symbol-value (intern (format nil "*SM-~A-INDEX*" s))) (!bootstrap-slot-index 'standard-reader-method s) (!bootstrap-slot-index 'standard-writer-method s) - (!bootstrap-slot-index 'standard-boundp-method s)))) + (!bootstrap-slot-index 'standard-boundp-method s) + (!bootstrap-slot-index 'global-reader-method s) + (!bootstrap-slot-index 'global-writer-method s) + (!bootstrap-slot-index 'global-boundp-method s)))) + +(define-symbol-macro *standard-method-classes* + (list *the-class-standard-method* *the-class-standard-reader-method* + *the-class-standard-writer-method* *the-class-standard-boundp-method* + *the-class-global-reader-method* *the-class-global-writer-method* + *the-class-global-boundp-method*)) (defun safe-method-specializers (method) - (let ((standard-method-classes - (list *the-class-standard-method* - *the-class-standard-reader-method* - *the-class-standard-writer-method* - *the-class-standard-boundp-method*)) + (let ((standard-method-classes *standard-method-classes*) (class (class-of method))) (if (member class standard-method-classes) (clos-slots-ref (get-slots method) *sm-specializers-index*) @@ -1827,21 +1935,13 @@ bootstrapping. (and (typep mf '%method-function) (%method-function-fast-function mf)))) (defun safe-method-function (method) - (let ((standard-method-classes - (list *the-class-standard-method* - *the-class-standard-reader-method* - *the-class-standard-writer-method* - *the-class-standard-boundp-method*)) + (let ((standard-method-classes *standard-method-classes*) (class (class-of method))) (if (member class standard-method-classes) (clos-slots-ref (get-slots method) *sm-%function-index*) (method-function method)))) (defun safe-method-qualifiers (method) - (let ((standard-method-classes - (list *the-class-standard-method* - *the-class-standard-reader-method* - *the-class-standard-writer-method* - *the-class-standard-boundp-method*)) + (let ((standard-method-classes *standard-method-classes*) (class (class-of method))) (if (member class standard-method-classes) (clos-slots-ref (get-slots method) *sm-qualifiers-index*) @@ -1908,6 +2008,7 @@ bootstrapping. (package (symbol-package symbol))) (and (or (eq package *pcl-package*) (memq package (package-use-list *pcl-package*))) + (not (eq package #.(find-package "CL"))) ;; FIXME: this test will eventually be ;; superseded by the *internal-pcl...* test, ;; above. While we are in a process of @@ -1944,6 +2045,7 @@ bootstrapping. lambda-list-p) argument-precedence-order source-location + documentation &allow-other-keys) (declare (ignore keys)) (cond ((and existing (early-gf-p existing)) @@ -1953,19 +2055,21 @@ bootstrapping. ((assoc spec *!generic-function-fixups* :test #'equal) (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))) + argument-precedence-order source-location + documentation) + (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 - argument-precedence-order source-location)))) + argument-precedence-order source-location + documentation)))) (defun make-early-gf (spec &optional lambda-list lambda-list-p - function argument-precedence-order source-location) + function argument-precedence-order source-location + documentation) (let ((fin (allocate-standard-funcallable-instance *sgf-wrapper* *sgf-slots-init*))) (set-funcallable-instance-function @@ -1981,15 +2085,18 @@ bootstrapping. has not been set." fin))))) (setf (gdefinition spec) fin) (!bootstrap-set-slot 'standard-generic-function fin 'name spec) - (!bootstrap-set-slot 'standard-generic-function - fin - 'source - source-location) + (!bootstrap-set-slot 'standard-generic-function fin + 'source source-location) + (!bootstrap-set-slot 'standard-generic-function fin + '%documentation documentation) (set-fun-name fin spec) (let ((arg-info (make-arg-info))) (setf (early-gf-arg-info fin) arg-info) (when lambda-list-p - (proclaim (defgeneric-declaration spec lambda-list)) + (setf (info :function :type spec) + (specifier-type + (ftype-declaration-from-lambda-list lambda-list spec)) + (info :function :where-from spec) :defined-method) (if argument-precedence-order (set-arg-info fin :lambda-list lambda-list @@ -2012,10 +2119,15 @@ bootstrapping. (let ((new-state (if (and dfun (or cache info)) (list* dfun cache info) dfun))) - (if (eq *boot-state* 'complete) - (setf (safe-gf-dfun-state gf) new-state) - (setf (clos-slots-ref (get-slots gf) *sgf-dfun-state-index*) - new-state))) + (cond + ((eq *boot-state* 'complete) + ;; Check that we are under the lock. + #+sb-thread + (aver (eq sb-thread:*current-thread* (sb-thread::spinlock-value (gf-lock gf)))) + (setf (safe-gf-dfun-state gf) new-state)) + (t + (setf (clos-slots-ref (get-slots gf) *sgf-dfun-state-index*) + new-state)))) dfun) (defun gf-dfun-cache (gf) @@ -2104,7 +2216,10 @@ bootstrapping. (prog1 (apply #'reinitialize-instance existing all-keys) (when lambda-list-p - (proclaim (defgeneric-declaration fun-name lambda-list))))) + (setf (info :function :type fun-name) + (specifier-type + (ftype-declaration-from-lambda-list lambda-list fun-name)) + (info :function :where-from fun-name) :defined-method)))) (defun real-ensure-gf-using-class--null (existing @@ -2120,7 +2235,10 @@ bootstrapping. (apply #'make-instance generic-function-class :name fun-name all-keys)) (when lambda-list-p - (proclaim (defgeneric-declaration fun-name lambda-list))))) + (setf (info :function :type fun-name) + (specifier-type + (ftype-declaration-from-lambda-list lambda-list fun-name)) + (info :function :where-from fun-name) :defined-method)))) (defun safe-gf-arg-info (generic-function) (if (eq (class-of generic-function) *the-class-standard-generic-function*) @@ -2155,7 +2273,8 @@ bootstrapping. arg-info))) (defun early-make-a-method (class qualifiers arglist specializers initargs doc - &key slot-name object-class method-class-function) + &key slot-name object-class method-class-function + definition-source) (let ((parsed ()) (unparsed ())) ;; Figure out whether we got class objects or class names as the @@ -2196,14 +2315,15 @@ bootstrapping. initargs doc) (when slot-name (list :slot-name slot-name :object-class object-class - :method-class-function method-class-function)))))) + :method-class-function method-class-function)) + (list :definition-source definition-source))))) (initialize-method-function initargs result) result))) (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)) + &rest args &key slot-name object-class method-class-function + definition-source) (if method-class-function (let* ((object-class (if (classp object-class) object-class (find-class object-class))) @@ -2219,6 +2339,7 @@ bootstrapping. (apply #'make-instance (apply method-class-function object-class slot-definition initargs) + :definition-source definition-source initargs))) (apply #'make-instance class :qualifiers qualifiers :lambda-list lambda-list :specializers specializers @@ -2276,11 +2397,10 @@ 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 + &key documentation definition-source + &allow-other-keys) (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 +2410,14 @@ 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 documentation + :definition-source definition-source))) + (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 +2524,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 +2564,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))) (defun extract-parameters (specialized-lambda-list) (multiple-value-bind (parameters ignore1 ignore2) @@ -2613,13 +2686,18 @@ bootstrapping. ;;; walker stuff was only used for implementing stuff like that; maybe ;;; it's not needed any more? Hunt down what it was used for and see. +(defun extract-the (form) + (cond ((and (consp form) (eq (car form) 'the)) + (aver (proper-list-of-length-p 3)) + (third form)) + (t + form))) + (defmacro with-slots (slots instance &body body) (let ((in (gensym))) `(let ((,in ,instance)) (declare (ignorable ,in)) - ,@(let ((instance (if (and (consp instance) (eq (car instance) 'the)) - (third instance) - instance))) + ,@(let ((instance (extract-the instance))) (and (symbolp instance) `((declare (%variable-rebinding ,in ,instance))))) ,in @@ -2641,9 +2719,7 @@ bootstrapping. (let ((in (gensym))) `(let ((,in ,instance)) (declare (ignorable ,in)) - ,@(let ((instance (if (and (consp instance) (eq (car instance) 'the)) - (third instance) - instance))) + ,@(let ((instance (extract-the instance))) (and (symbolp instance) `((declare (%variable-rebinding ,in ,instance))))) ,in