@@ -44,30 +44,19 @@ At compile-time *COMPILER-MACRO-EXPANDING-P* is bound to non-NIL."
44
44
(setq documentation (constant-form-value documentation env)))
45
45
(when docp (check-type documentation string ))
46
46
(let* ((*name* name)
47
- (*environment* env)
48
47
(untyped-lambda-list (normalize-untyped-lambda-list untyped-lambda-list))
49
- (untyped-lambda-list (if (member ' &key untyped-lambda-list)
50
- (let* ((key-position (position ' &key untyped-lambda-list)))
51
- (append (subseq untyped-lambda-list 0 key-position)
52
- ' (&key )
53
- (sort (subseq untyped-lambda-list (1+ key-position))
54
- #' string<
55
- :key (lambda (param)
56
- (if (and (listp param)
57
- (null (cddr param)))
58
- (car param)
59
- param)))))
60
- untyped-lambda-list)))
48
+ (untyped-lambda-list (sort-untyped-lambda-list untyped-lambda-list)))
61
49
` (progn
62
50
(eval-when (:compile-toplevel :load-toplevel :execute )
63
- , (when overwrite
64
- ` (undefine-polymorphic-function ' ,name))
51
+ , (when overwrite ` (undefine-polymorphic-function ' ,name))
65
52
(register-polymorphic-function ' ,name ' ,untyped-lambda-list , documentation
66
53
, default
67
54
:source #+ sbcl (sb-c :source-location) #- sbcl nil
68
55
:declaration , dispatch-declaration)
69
- #+ sbcl (sb-c :defknown , name * * nil :overwrite-fndb-silently t ))
70
- (fdefinition ' ,name))))
56
+ #+ sbcl (sb-c :defknown , name * * nil :overwrite-fndb-silently t )
57
+ , (when (fboundp ' pf-compiler-macro)
58
+ ` (setf (compiler-macro-function ' ,name) #' pf-compiler-macro))
59
+ (fdefinition ' ,name)))))
71
60
72
61
(defun extract-declarations (body &key documentation )
73
62
" Returns two values: DECLARATIONS and remaining BODY
@@ -118,7 +107,99 @@ If DOCUMENTATION is non-NIL, returns three values: DECLARATIONS and remaining BO
118
107
existing-effective-type-list)
119
108
(undefpolymorph name existing-type-list))))
120
109
121
- (defmacro defpolymorph (name typed-lambda-list return-type
110
+ (defun expand-defpolymorph-lite
111
+ (name typed-lambda-list return-type body env)
112
+ (destructuring-bind
113
+ (name &rest keys
114
+ &key invalidate-pf (static-dispatch-name nil static-dispatch-name-p)
115
+ &allow-other-keys )
116
+ (if (typep name ' function-name)
117
+ (list name)
118
+ name)
119
+ (declare (type function-name name)
120
+ (optimize debug ))
121
+ (remf keys :invalidate-pf )
122
+ (remf keys :static-dispatch-name )
123
+ (assert (null keys) ()
124
+ " The only legal options for DEFPOLYMORPH are:~% STATIC-DISPATCH-NAME and INVALIDATE-PF~% Did you intend to polymorphic-functions instead of polymorphic-functions-lite?" )
125
+ (let+ ((block-name (blockify-name name))
126
+ (*environment* env)
127
+ ((&values unsorted-typed-lambda-list ignorable-list)
128
+ (normalize-typed-lambda-list typed-lambda-list))
129
+ (typed-lambda-list (sort-typed-lambda-list unsorted-typed-lambda-list))
130
+ (untyped-lambda-list (untyped-lambda-list typed-lambda-list))
131
+ (pf-lambda-list (may-be-pf-lambda-list name untyped-lambda-list))
132
+ (parameters (make-polymorph-parameters-from-lambda-lists
133
+ pf-lambda-list typed-lambda-list))
134
+ (lambda-list-type (lambda-list-type typed-lambda-list :typed t ))
135
+ ((&values param-list type-list effective-type-list)
136
+ (polymorph-effective-lambda-list parameters))
137
+ ((&values declarations body doc)
138
+ (extract-declarations body :documentation t ))
139
+ (static-dispatch-name
140
+ (if static-dispatch-name-p
141
+ static-dispatch-name
142
+ (make-or-retrieve-static-dispatch-name name type-list)))
143
+ (lambda-declarations (lambda-declarations parameters))
144
+ ((&values ensure-type-form return-type)
145
+ (ensure-type-form return-type block-name body
146
+ :variable
147
+ (remove-duplicates
148
+ (remove-if
149
+ #' null
150
+ (mapcar #' third
151
+ (rest lambda-declarations))))
152
+ :declare
153
+ (remove-duplicates
154
+ (rest lambda-declarations)
155
+ :test #' equal )))
156
+ (lambda-body
157
+ ` (list-named-lambda (polymorph , name , type-list)
158
+ , (symbol-package block-name)
159
+ , param-list
160
+ (declare (ignorable ,@ ignorable-list))
161
+ , lambda-declarations
162
+ , declarations
163
+ , ensure-type-form))
164
+ ; ; LAMBDA-BODY contains the ENSURE-TYPE-FORM that performs
165
+ ; ; run time checks on the return types.
166
+ (ftype-proclaimation
167
+ (ftype-proclaimation
168
+ static-dispatch-name effective-type-list return-type env)))
169
+
170
+ ` (eval-when (:compile-toplevel :load-toplevel :execute )
171
+
172
+ (unless (and (fboundp ' ,name)
173
+ (typep (function , name) ' polymorphic-function))
174
+ (define-polymorphic-function , name , untyped-lambda-list))
175
+
176
+ (setf (fdefinition ' ,static-dispatch-name) , lambda-body)
177
+ , ftype-proclaimation
178
+ (register-polymorph ' ,name nil
179
+ ' ,doc
180
+ ' ,typed-lambda-list
181
+ ' ,type-list
182
+ ' ,effective-type-list
183
+ nil
184
+ nil
185
+ ' ,return-type
186
+ nil
187
+ ' ,static-dispatch-name
188
+ ' ,lambda-list-type
189
+ ' ,(run-time-applicable-p-form parameters)
190
+ nil
191
+ #+ sbcl (sb-c :source-location))
192
+ , (when invalidate-pf
193
+ ` (invalidate-polymorphic-function-lambda (fdefinition ' ,name)))
194
+ ' ,name))))
195
+
196
+ ; ;; CLHS recommends that
197
+ ; ;; Macros intended for use in top level forms should be written so that
198
+ ; ;; side-effects are done by the forms in the macro expansion. The
199
+ ; ;; macro-expander itself should not do the side-effects.
200
+ ; ;; Reference: http://clhs.lisp.se/Body/s_eval_w.htm
201
+
202
+ (defmacro defpolymorph (&whole whole name typed-lambda-list return-type
122
203
&body body &environment env)
123
204
" Expects OPTIONAL or KEY args to be in the form
124
205
@@ -135,136 +216,29 @@ If DOCUMENTATION is non-NIL, returns three values: DECLARATIONS and remaining BO
135
216
- If INVALIDATE-PF is non-NIL then the associated polymorphic-function
136
217
is forced to recompute its dispatching after this polymorph is defined.
137
218
"
138
- (destructuring-bind (name
139
- &key
140
- (static-dispatch-name nil static-dispatch-name-p)
141
- invalidate-pf)
142
- (if (typep name ' function-name)
143
- (list name)
144
- name)
145
- (declare (type function-name name)
146
- (optimize debug ))
147
- (let+ ((block-name (blockify-name name))
148
- (*environment* env)
149
- ((&values unsorted-typed-lambda-list ignorable-list)
150
- (normalize-typed-lambda-list typed-lambda-list))
151
- (typed-lambda-list (if (member ' &key unsorted-typed-lambda-list)
152
- (let ((key-position
153
- (position ' &key
154
- unsorted-typed-lambda-list)))
155
- (append (subseq unsorted-typed-lambda-list
156
- 0 key-position)
157
- ' (&key )
158
- (sort (subseq unsorted-typed-lambda-list
159
- (1+ key-position))
160
- #' string<
161
- :key #' caar )))
162
- unsorted-typed-lambda-list))
163
- (untyped-lambda-list (untyped-lambda-list typed-lambda-list))
164
- (pf-lambda-list (if (and (fboundp name)
165
- (typep (fdefinition name) ' polymorphic-function))
166
- (mapcar (lambda (elt )
167
- (if (atom elt ) elt (first elt )))
168
- (polymorphic-function-lambda-list
169
- (fdefinition name)))
170
- untyped-lambda-list))
171
- (parameters (make-polymorph-parameters-from-lambda-lists
172
- pf-lambda-list typed-lambda-list))
173
- (lambda-list-type (lambda-list-type typed-lambda-list :typed t )))
174
- (declare (type typed-lambda-list typed-lambda-list))
175
-
176
- ; ; USE OF INTERN BELOW:
177
- ; ; We do want STATIC-DISPATCH-NAME symbol collision to actually take place
178
- ; ; when type lists of two polymorphs are "equivalent".
179
- ; ; (Credits to phoe for pointing out in the issue at
180
- ; ; https://github.com/digikar99/polymorphic-functions/issues/3)
181
- ; ; Consider a file A to be
182
- ; ; compiled before restarting a lisp image, and file B after the
183
- ; ; restart. The use of GENTEMP meant that two "separate" compilations of
184
- ; ; the same polymorph in the two files, could result in different
185
- ; ; STATIC-DISPATCH-NAMEs. If the two files were then loaded
186
- ; ; simultaneously, and the polymorphs static-dispatched at some point,
187
- ; ; then there remained the possibility that different static-dispatches
188
- ; ; could be using "different versions" of the polymorph.
189
- ; ; Thus, we actually do want collisions to take place so that a same
190
- ; ; deterministic/latest version of the polymorph is called; therefore we
191
- ; ; use INTERN.
192
- (let+ (((&values param-list type-list effective-type-list)
193
- (polymorph-effective-lambda-list parameters))
194
- ((&values declarations body doc)
195
- (extract-declarations body :documentation t ))
196
- (static-dispatch-name
197
- (if static-dispatch-name-p
198
- static-dispatch-name
199
- (let* ((p-old
200
- (and (fboundp name)
201
- (typep (fdefinition name)
202
- ' polymorphic-function)
203
- (find-polymorph name type-list)))
204
- (old-name
205
- (when p-old
206
- (polymorph-static-dispatch-name
207
- p-old))))
208
- (if old-name
209
- old-name
210
- (let ((*package* (find-package
211
- ' #:polymorphic-functions.nonuser)))
212
- (intern (write-to-string
213
- ` (polymorph , name , type-list))
214
- ' #:polymorphic-functions.nonuser))))))
215
- (lambda-declarations (lambda-declarations parameters))
216
- (lambda-body
217
- ` (list-named-lambda (polymorph , name , type-list)
218
- , (symbol-package block-name)
219
- , param-list
220
- (declare (ignorable ,@ ignorable-list))
221
- , lambda-declarations
222
- , declarations
223
- , (multiple-value-bind (form form-return-type)
224
- (ensure-type-form return-type
225
- ` (block , block-name
226
- (locally ,@ body))
227
- env)
228
- (setq return-type form-return-type)
229
- form))))
230
- ` (eval-when (:compile-toplevel :load-toplevel :execute )
231
- (unless (and (fboundp ' ,name)
232
- (typep (function , name) ' polymorphic-function))
233
- (define-polymorphic-function , name , untyped-lambda-list))
234
- (setf (fdefinition ' ,static-dispatch-name) , lambda-body)
235
- , (let* ((ftype (ftype-for-static-dispatch static-dispatch-name
236
- effective-type-list
237
- return-type
238
- env))
239
- (proclaimation
240
- ` (proclaim ' ,ftype)))
241
- (if optim-debug
242
- proclaimation
243
- ` (handler-bind ((warning #' muffle-warning ))
244
- , proclaimation)))
245
- (register-polymorph ' ,name nil
246
- ' ,doc
247
- ' ,typed-lambda-list
248
- ' ,type-list
249
- ' ,effective-type-list
250
- nil
251
- nil
252
- ' ,return-type
253
- nil
254
- ' ,static-dispatch-name
255
- ' ,lambda-list-type
256
- ' ,(run-time-applicable-p-form parameters)
257
- , (compiler-applicable-p-lambda-body parameters)
258
- #+ sbcl (sb-c :source-location))
259
- , (when invalidate-pf
260
- ` (invalidate-polymorphic-function-lambda (fdefinition ' ,name)))
261
- ' ,name)))))
219
+ (if (fboundp ' pf-compiler-macro)
220
+ (uiop :symbol-call ' #:polymorphic-functions
221
+ ' #:expand-defpolymorph-full
222
+ whole name typed-lambda-list return-type body env)
223
+ (expand-defpolymorph-lite name typed-lambda-list return-type body env)))
262
224
263
225
(defun undefpolymorph (name type-list)
264
226
" Remove the POLYMORPH associated with NAME with TYPE-LIST"
227
+ ; ; FIXME: Undefining polymorphs can also lead to polymorph call ambiguity.
228
+ ; ; One (expensive) solution is to insert afresh the type lists of all polymorphs
229
+ ; ; to resolve it.
230
+ #+ sbcl
231
+ (let ((info (sb-c ::fun-info-or-lose name))
232
+ (ctype (sb-c ::specifier-type (list ' function type-list ' *))))
233
+ (setf (sb-c ::fun-info-transforms info)
234
+ (remove-if (curry #' sb-c::type= ctype)
235
+ (sb-c ::fun-info-transforms info)
236
+ :key #' sb-c::transform-type)))
265
237
(remove-polymorph name type-list)
266
238
(update-polymorphic-function-lambda (fdefinition name) t ))
267
239
268
240
(defun undefine-polymorphic-function (name)
269
241
" Remove the POLYMORPH(-WRAPPER) defined by DEFINE-POLYMORPH"
270
- (fmakunbound name))
242
+ (fmakunbound name)
243
+ #+ sbcl (sb-c ::undefine-fun-name name)
244
+ (setf (compiler-macro-function name) nil ))
0 commit comments