Skip to content

Commit 3aac7bf

Browse files
committed
let-values: avoid rebinding, better declarations
the let* equivalents now avoid rebinding at the end which can avoid unused variable warnings in cases where a binding is only used by later bindings. It now uses process-declarations to do the declaration processing Also some fixes to the sysdcls which had got out of date
1 parent a1fab80 commit 3aac7bf

File tree

5 files changed

+106
-74
lines changed

5 files changed

+106
-74
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -2575,7 +2575,7 @@ Declarations should be handled properly (`(declare (type fixnum ...))` is better
25752575

25762576
In the `let*`-style cases the declarations will apply to all duplicate variables.
25772577

2578-
`special` declarations are an interesting case for sequenctial binding forms. Consider this form:
2578+
`special` declarations are an interesting case for sequential binding forms. Consider this form:
25792579

25802580
```lisp
25812581
(let*-values (((a) 1)
@@ -2587,7 +2587,7 @@ In the `let*`-style cases the declarations will apply to all duplicate variables
25872587
Now, without knowing what `f` does, it could refer to the dynamic binding of `a`. So the special declaration for `a` needs to be made for the temporary binding as well, unless it is in the final group of bindings. The starred forms now do this.
25882588

25892589
### Package, module, dependencies
2590-
`let-values` lives in and provides `:org.tfeb.hax.let-values`. It requires `spam`, `collecting`, `iterate` and `utilities`, and will attempt to load them if `require-module` is present.
2590+
`let-values` lives in and provides `:org.tfeb.hax.let-values`. It requires `spam`, `collecting`, `iterate` `utilities` and `process-declarations`, and will attempt to load them if `require-module` is present.
25912591

25922592
## Processing declaration specifiers: `process-declarations`
25932593
When writing macros it's useful to be able to process declaration specifiers in a standardised way. In particular it's common to want to select all specifiers which mention a variable and perhaps create equivalent ones which refer to some new variable introduced by the macro.

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
10.0.0
1+
10.1.0

let-values.lisp

+94-69
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
(:org.tfeb.hax.spam :compile t)
77
(:org.tfeb.hax.collecting :compile t)
88
(:org.tfeb.hax.iterate :compile t)
9-
(:org.tfeb.hax.utilities :compile t))
9+
(:org.tfeb.hax.utilities :compile t)
10+
(:org.tfeb.hax.process-declarations :compile t))
1011

1112
(defpackage :org.tfeb.hax.let-values
1213
(:use :cl)
1314
(:use
1415
:org.tfeb.hax.spam
1516
:org.tfeb.hax.collecting
1617
:org.tfeb.hax.iterate
17-
:org.tfeb.hax.utilities)
18+
:org.tfeb.hax.utilities
19+
:org.tfeb.hax.process-declarations)
1820
(:export
1921
#:let-values
2022
#:let*-values
@@ -25,6 +27,14 @@
2527

2628
(provide :org.tfeb.hax.let-values)
2729

30+
(define-condition let-values-error (program-error simple-error)
31+
())
32+
33+
(defun let-values-error (control &rest arguments)
34+
(error 'let-values-error
35+
:format-control control
36+
:format-arguments arguments))
37+
2838
(defun make-vme (name starred)
2939
(cons name (if starred name (make-symbol (symbol-name name)))))
3040

@@ -34,47 +44,52 @@
3444
(defun vme-hidden (vme)
3545
(cdr vme))
3646

37-
(defun mapped-variable-declarations (declarations varmap environment special-too)
38-
;; Appropriate variable declarations from DECLARATIONS mapped
39-
;; through VARMAP 'Appropriate' means type declarations and
47+
(defun mapped-variable-declarations (declarations varmap environment special-too complement)
48+
;; Return the appropriate variable declarations from DECLARATIONS
49+
;; mapped through VARMAP. With COMPLEMENT return the complement of
50+
;; this set. 'Appropriate' means type declarations and
4051
;; dynamic-extent declarations. if SPECIAL-TOO is given also map
41-
;; those (this matters for non-funal groups in sequential binding
42-
;; constructs), since later initforms can refer to the special binding.
43-
(flet ((mapped-variables (varmap variables)
44-
(collecting
45-
(dolist (vme varmap)
46-
(when (member (vme-name vme) variables)
47-
(collect (vme-hidden vme)))))))
48-
(collecting
49-
(dolist (declaration declarations)
50-
(dolist (specifier (mapcar (lambda (d)
51-
(canonicalize-declaration-specifier d environment))
52-
(rest declaration)))
53-
(destructuring-bind (identifier . rest) specifier
54-
(case identifier
55-
(type
56-
(destructuring-bind (type . vars) rest
57-
(let ((mapped-variables (mapped-variables varmap vars)))
58-
(unless (null mapped-variables)
59-
(collect `(declare (type ,type ,@mapped-variables)))))))
60-
(dynamic-extent
61-
(let ((mapped-variables (mapped-variables varmap rest)))
62-
(unless (null mapped-variables)
63-
(collect `(declare (,identifier ,@mapped-variables))))))
64-
(special
65-
(when special-too
66-
(let ((mapped-variables (mapped-variables varmap rest)))
67-
(unless (null mapped-variables)
68-
(collect `(declare (special ,@mapped-variables))))))))))))))
52+
;; those. This matters for non-final groups in sequential binding
53+
;; constructs, since later initforms can refer to the special
54+
;; binding.
55+
(multiple-value-bind (selected others)
56+
(with-collectors (select other)
57+
(dolist (declaration declarations)
58+
(dolist (specifier (rest declaration))
59+
(processing-declaration-specifier (specifier :bindings ((variable-names '()))
60+
:identifier identifier
61+
:constructor maker
62+
:environment environment)
63+
(if (and (not (null variable-names))
64+
(or special-too (not (eq identifier 'special))))
65+
(multiple-value-bind (hits misses)
66+
(with-collectors (hit miss)
67+
(dolist (variable variable-names)
68+
(let ((found (find variable varmap :key #'vme-name)))
69+
(if found
70+
(hit (vme-hidden found))
71+
(miss variable)))))
72+
(unless (null hits)
73+
(select (maker :variable-names hits)))
74+
(unless (null misses)
75+
(other (maker :variable-names misses))))
76+
(other specifier))))))
77+
(if (not complement)
78+
(if (not (null selected))
79+
`((declare ,@selected))
80+
'())
81+
(if (not (null others))
82+
`((declare ,@others))
83+
'()))))
6984

7085
(defun expand-lv (clauses decls/forms starred environment &aux (unique-variables '()))
7186
(unless (matchp clauses (list-of (some-of (list-matches (list-of (var)) (any))
7287
(list-matches (list-of (var))))))
73-
(error "bad clauses ~S" clauses))
88+
(let-values-error "Bad let-values clauses ~S" (list clauses)))
7489
(if (null clauses)
7590
`(locally ,@decls/forms)
76-
(multiple-value-bind (varmaps vfs)
77-
(with-collectors (varmap vf)
91+
(multiple-value-bind (varmaps vfs whole-varmap)
92+
(with-collectors (varmap vf whole-varmap-entry)
7893
(dolist (clause clauses)
7994
(varmap
8095
(with-collectors (vme)
@@ -83,25 +98,29 @@
8398
(dolist (var vars)
8499
(unless starred
85100
(when (member var unique-variables)
86-
(error "~S is not unique" var))
101+
(let-values-error "Variable ~S is not unique" var))
87102
(push var unique-variables))
88-
(vme (make-vme var starred))))))))
89-
(assert (= (length varmaps) (length vfs)) () "botched")
90-
(let ((declarations (nth-value 0 (parse-simple-body decls/forms))))
91-
(iterate mvb ((vms varmaps) (forms vfs))
103+
(let ((vme (make-vme var starred)))
104+
(vme vme) (whole-varmap-entry vme))))))))
105+
(assert (= (length varmaps) (length vfs)) () "botched") ;our fault
106+
(multiple-value-bind (declarations forms) (parse-simple-body decls/forms)
107+
(iterate mvb ((vms varmaps) (initforms vfs))
92108
(destructuring-bind (vm . more-vms) vms
93-
(destructuring-bind (form . more-forms) forms
94-
`(multiple-value-bind ,(mapcar #'vme-hidden vm) ,form
95-
,@(mapped-variable-declarations declarations vm environment
96-
(and starred (not (null more-vms))))
97-
,(if (not (null more-vms))
98-
(mvb more-vms more-forms)
99-
`(let ,(mapcan (lambda (varmap)
100-
(mapcar (lambda (vme)
101-
`(,(vme-name vme) ,(vme-hidden vme)))
102-
varmap))
103-
varmaps)
104-
,@decls/forms))))))))))
109+
(destructuring-bind (initform . more-initforms) initforms
110+
`(multiple-value-bind ,(mapcar #'vme-hidden vm) ,initform
111+
,@(mapped-variable-declarations declarations vm environment starred nil)
112+
,@(cond
113+
((not (null more-vms))
114+
`(,(mvb more-vms more-initforms)))
115+
((not starred)
116+
`((let ,(mapcar (lambda (vme)
117+
`(,(vme-name vme) ,(vme-hidden vme)))
118+
whole-varmap)
119+
,@declarations
120+
,@forms)))
121+
(starred
122+
`(,@(mapped-variable-declarations declarations whole-varmap environment t t)
123+
,@forms)))))))))))
105124

106125
(defmacro let-values ((&rest clauses) &body decls/forms &environment environment)
107126
"Multiple-value LET form with parallel binding
@@ -145,11 +164,11 @@ Declarations should be handled correctly and perhaps usefully."
145164

146165
(defun expand-lv* (clauses decls/forms starred environment &aux (unique-variables '()))
147166
(unless (matchp clauses (list-of (list*-matches (list-of (var)) (any))))
148-
(error "bad clauses ~S" clauses))
167+
(let-values-error "bad let-values* clauses ~S" clauses))
149168
(if (null clauses)
150169
`(locally ,@decls/forms)
151-
(multiple-value-bind (varmaps vis)
152-
(with-collectors (varmap vi)
170+
(multiple-value-bind (varmaps vis whole-varmap)
171+
(with-collectors (varmap vi whole-varmap-entry)
153172
(dolist (clause clauses)
154173
(varmap
155174
(with-collectors (vme)
@@ -158,25 +177,31 @@ Declarations should be handled correctly and perhaps usefully."
158177
(dolist (var vars)
159178
(unless starred
160179
(when (member var unique-variables)
161-
(error "~S is not unique" var))
180+
(let-values-error "Variable ~S is not unique" var))
162181
(push var unique-variables))
163-
(vme (make-vme var starred))))))))
164-
(assert (= (length varmaps) (length vis)) () "botched")
165-
(let ((declarations (nth-value 0 (parse-simple-body decls/forms))))
182+
(let ((vme (make-vme var starred)))
183+
(vme vme) (whole-varmap-entry vme))))))))
184+
(assert (= (length varmaps) (length vis)) () "botched") ;our fault
185+
(multiple-value-bind (declarations forms) (parse-simple-body decls/forms)
166186
(iterate mvc ((vms varmaps) (initforms vis))
167187
(destructuring-bind (vm . more-vms) vms
168188
(destructuring-bind (this-initforms . more-initforms) initforms
169189
`(multiple-value-call
170190
(lambda ,(mapcar #'vme-hidden vm)
171-
,@(mapped-variable-declarations declarations vm environment nil)
172-
,(if (not (null more-vms))
173-
(mvc more-vms more-initforms)
174-
`(let ,(mapcan (lambda (varmap)
175-
(mapcar (lambda (vme)
176-
`(,(vme-name vme) ,(vme-hidden vme)))
177-
varmap))
178-
varmaps)
179-
,@decls/forms)))
191+
,@(mapped-variable-declarations declarations vm environment starred nil)
192+
,@(cond
193+
((not (null more-vms))
194+
`(,(mvc more-vms more-initforms)))
195+
((not starred)
196+
`((let ,(mapcar (lambda (vme)
197+
`(,(vme-name vme) ,(vme-hidden vme)))
198+
whole-varmap)
199+
,@declarations
200+
,@forms)))
201+
(starred
202+
`(,@(mapped-variable-declarations declarations whole-varmap environment
203+
t t)
204+
,@forms))))
180205
,@this-initforms))))))))
181206

182207
(defmacro let-values* ((&rest clauses) &body decls/forms &environment environment)

org.tfeb.hax.asd

+7-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
(:file "slog"
4141
:depends-on ("simple-loops" "collecting" "spam"
4242
"metatronic"))
43+
(:file "let-values"
44+
:depends-on ("spam" "collecting" "iterate"
45+
"utilities" "process-declarations"))
46+
(:file "process-declarations"
47+
:depends-on ("utilities"))
4348
(:file "hax-cometh"
4449
:depends-on ("collecting" "wrapping-standard"
4550
"iterate" "dynamic-state" "memoize"
@@ -48,7 +53,8 @@
4853
"define-functions" "trace-macroexpand"
4954
"binding" "stringtable" "object-accessors"
5055
"utilities" "simple-loops" "spam"
51-
"metatronic" "slog"))))
56+
"metatronic" "slog" "let-values"
57+
"process-declarations"))))
5258

5359
(defsystem "org.tfeb.hax/test"
5460
:description "TFEB hax tests"

org.tfeb.hax.let-values.asd

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
("org.tfeb.hax.spam"
1919
"org.tfeb.hax.collecting"
2020
"org.tfeb.hax.iterate"
21-
"org.tfeb.hax.utilities")
21+
"org.tfeb.hax.utilities"
22+
"org.tfeb.hax.process-declarations")
2223
:components
2324
((:file "let-values")))

0 commit comments

Comments
 (0)