1 ;;;; pcl-patch.lisp --- Hot-patch for SBCL's PCL variant.
3 ;;;; Copyright (C) 2014 Jan Moringen
5 ;;;; Author: Jan Moringen <jmoringe@techfak.uni-bielefeld.de>
7 (cl:in-package #:sb-pcl)
9 ;;; `make-method-lambda-using-specializers'
11 (export '(make-method-lambda-using-specializers))
13 (defgeneric make-method-lambda-using-specializers (gf method qualifiers specializers method-lambda env)
14 (:method ((gf standard-generic-function) (method standard-method) qualifiers specializers method-lambda env)
15 (declare (type (cons (eql lambda) (cons list)) method-lambda))
16 ;; Default behavior: delegate to MAKE-METHOD-LAMBDA.
17 (let* ((lambda-list (second method-lambda))
18 (*method-lambda-list* (append
19 (mapcar #'list (subseq lambda-list 0 (length specializers)) specializers)
20 (subseq lambda-list (length specializers)))))
21 (make-method-lambda gf method method-lambda env)))
27 2. initargs for the method instance
28 3. a (possibly modified) method lambda-list or nil"))
30 (defun expand-defmethod (name
37 (multiple-value-bind (parameters unspecialized-lambda-list specializers)
38 (parse-specialized-lambda-list lambda-list)
39 (declare (ignore parameters))
40 (let ((*method-name* `(,name ,@qualifiers ,specializers))
41 (method-lambda `(lambda ,unspecialized-lambda-list ,@body)))
42 (multiple-value-bind (method-function-lambda initargs new-lambda-list)
43 (make-method-lambda-using-specializers
44 proto-gf proto-method qualifiers specializers method-lambda env)
45 (let ((initargs-form (make-method-initargs-form
46 proto-gf proto-method method-function-lambda
48 (specializers-form (make-method-specializers-form
49 proto-gf proto-method specializers env)))
51 ;; Note: We could DECLAIM the ftype of the generic function
52 ;; here, since ANSI specifies that we create it if it does
53 ;; not exist. However, I chose not to, because I think it's
54 ;; more useful to support a style of programming where every
55 ;; generic function has an explicit DEFGENERIC and any typos
56 ;; in DEFMETHODs are warned about. Otherwise
58 ;; (DEFGENERIC FOO-BAR-BLETCH (X))
59 ;; (DEFMETHOD FOO-BAR-BLETCH ((X HASH-TABLE)) ..)
60 ;; (DEFMETHOD FOO-BRA-BLETCH ((X SIMPLE-VECTOR)) ..)
61 ;; (DEFMETHOD FOO-BAR-BLETCH ((X VECTOR)) ..)
62 ;; (DEFMETHOD FOO-BAR-BLETCH ((X ARRAY)) ..)
63 ;; (DEFMETHOD FOO-BAR-BLETCH ((X LIST)) ..)
65 ;; compiles without raising an error and runs without
66 ;; raising an error (since SIMPLE-VECTOR cases fall through
67 ;; to VECTOR) but still doesn't do what was intended. I hate
68 ;; that kind of bug (code which silently gives the wrong
69 ;; answer), so we don't do a DECLAIM here. -- WHN 20000229
70 ,(make-defmethod-form name qualifiers specializers-form
71 (or new-lambda-list unspecialized-lambda-list)
73 (class-name (class-of proto-method))
77 ;;; `make-specializer-form-using-class'
79 ;;; To free every new custom generic function class from having to
80 ;;; implement iteration over specializers in
81 ;;; `make-method-specializers-form', we provide a default method
83 ;;; make-method-specializers-form standard-g-f standard-method
85 ;;; which performs this iteration and calls the generic function
87 ;;; make-specializer-form-using-class proto-g-f proto-m specializer-names env
89 ;;; on which custom generic function classes can install methods to
90 ;;; handle their custom specializers. The generic function uses OR
91 ;;; method combination to allow the following idiom:
93 ;;; (defmethod make-specializer-form-using-class or
94 ;;; (proto-generic-function MY-GENERIC-FUNCTION)
95 ;;; (proto-method standard-method)
96 ;;; (specializer-name cons)
98 ;;; (when (typep specializer-name '(cons (eql MY-SPECIALIZER)))
99 ;;; MY-SPECIALIZER-FORM))
101 ;;; The OR method combination lets everything but (my-specializer …)
102 ;;; fall through to the next methods which will, at some point, handle
103 ;;; class and eql specializers and eventually reach an error signaling
104 ;;; method for invalid specializers.
106 (defmethod make-method-specializers-form
107 ((proto-generic-function standard-generic-function)
108 (proto-method standard-method)
109 (specializer-names t)
111 (flet ((make-parse-form (name)
112 (make-specializer-form-using-class
113 proto-generic-function proto-method name environment)))
114 `(list ,@(mapcar #'make-parse-form specializer-names))))
116 ;; TODO same approach for parse-specializer-using-class?
117 (defgeneric make-specializer-form-using-class (proto-generic-function proto-method specializer-name environment)
118 (:method-combination or)
121 "Return a form which, when evaluated in lexical environment
122 ENVIRONMENT, parses the specializer SPECIALIZER-NAME and returns
123 the appropriate specializer object.
125 Both PROTO-GENERIC-FUNCTION and PROTO-METHOD may be
126 uninitialized. However their types and prototype can be
129 ;; Default behavior is signaling an error for not otherwise handled
131 (defmethod make-specializer-form-using-class or
132 ((proto-generic-function standard-generic-function)
133 (proto-method standard-method)
136 (error 'simple-reference-error
138 "~@<~S is not a valid parameter specializer name.~@:>"
139 :format-arguments (list specializer-name)
140 :references (list '(:ansi-cl :macro defmethod)
141 '(:ansi-cl :glossary "parameter specializer name"))))
143 (defmethod make-specializer-form-using-class or
144 ((proto-generic-function standard-generic-function)
145 (proto-method standard-method)
146 (specializer-name specializer)
150 (defmethod make-specializer-form-using-class or
151 ((proto-generic-function standard-generic-function)
152 (proto-method standard-method)
153 (specializer-name symbol)
155 `(find-class ',specializer-name))
157 (defmethod make-specializer-form-using-class or
158 ((proto-generic-function standard-generic-function)
159 (proto-method standard-method)
160 (specializer-name cons)
162 ;; In case of unknown specializer or known specializer with syntax
163 ;; error, TYPECASE may fall through to default method with error
165 (typecase specializer-name
166 ((cons (eql eql) (cons t null))
167 `(intern-eql-specializer ,(second specializer-name)))
168 ((cons (eql class-eq) (cons t null))
169 `(class-eq-specializer (find-class ',(second specializer-name))))))