aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2011-03-09 22:48:44 -0500
committerStefan Monnier2011-03-09 22:48:44 -0500
commit6c075cd7c07d8f7f2ae52ab4369e709d7664043e (patch)
tree6b3defb08f7f0cb78f48d7fed4a7ef55d09426bc
parent0d6459dfb52188481bfd6bb53f1b2f653ecd6a5d (diff)
downloademacs-6c075cd7c07d8f7f2ae52ab4369e709d7664043e.tar.gz
emacs-6c075cd7c07d8f7f2ae52ab4369e709d7664043e.zip
Rewrite the cconv conversion algorithm, for clarity.
* lisp/emacs-lisp/byte-opt.el (byte-compile-inline-expand): Adjust check for new byte-code representation. * lisp/emacs-lisp/cconv.el (cconv--convert-function): Rename from cconv-closure-convert-function. (cconv-convert): Rename from cconv-closure-convert-rec. (cconv--analyse-use): Rename from cconv-analyse-use. (cconv--analyse-function): Rename from cconv-analyse-function. (cconv--analyse-use): Change some patterns to silence compiler. (cconv-convert, cconv--convert-function): Rewrite. * test/automated/lexbind-tests.el: New file.
-rw-r--r--doc/lispref/ChangeLog68
-rw-r--r--lisp/ChangeLog13
-rw-r--r--lisp/emacs-lisp/byte-opt.el3
-rw-r--r--lisp/emacs-lisp/cconv.el646
-rw-r--r--test/ChangeLog4
-rw-r--r--test/automated/lexbind-tests.el75
6 files changed, 373 insertions, 436 deletions
diff --git a/doc/lispref/ChangeLog b/doc/lispref/ChangeLog
index 2aecc5a6b4b..ab993fe35a2 100644
--- a/doc/lispref/ChangeLog
+++ b/doc/lispref/ChangeLog
@@ -1,34 +1,34 @@
12011-03-01 Stefan Monnier <monnier@iro.umontreal.ca> 12011-03-01 Stefan Monnier <monnier@iro.umontreal.ca>
2 2
3 * variables.texi (Scope): Mention the availability of lexical scoping. 3 * variables.texi (Scope): Mention the availability of lexical scoping.
4 (Lexical Binding): New node. 4 (Lexical Binding): New node.
5 * eval.texi (Eval): Add `eval's new `lexical' arg. 5 * eval.texi (Eval): Add `eval's new `lexical' arg.
6 6
72011-02-25 Stefan Monnier <monnier@iro.umontreal.ca> 72011-02-25 Stefan Monnier <monnier@iro.umontreal.ca>
8 8
9 * vol2.texi (Top): 9 * vol2.texi (Top):
10 * vol1.texi (Top): 10 * vol1.texi (Top):
11 * objects.texi (Programming Types, Funvec Type, Type Predicates): 11 * objects.texi (Programming Types, Funvec Type, Type Predicates):
12 * functions.texi (Functions, What Is a Function, Function Currying): 12 * functions.texi (Functions, What Is a Function, Function Currying):
13 * elisp.texi (Top): Remove mentions of funvec and curry. 13 * elisp.texi (Top): Remove mentions of funvec and curry.
14 14
15;; Local Variables: 15;; Local Variables:
16;; coding: utf-8 16;; coding: utf-8
17;; End: 17;; End:
18 18
19 Copyright (C) 2011 Free Software Foundation, Inc. 19 Copyright (C) 2011 Free Software Foundation, Inc.
20 20
21 This file is part of GNU Emacs. 21 This file is part of GNU Emacs.
22 22
23 GNU Emacs is free software: you can redistribute it and/or modify 23 GNU Emacs is free software: you can redistribute it and/or modify
24 it under the terms of the GNU General Public License as published by 24 it under the terms of the GNU General Public License as published by
25 the Free Software Foundation, either version 3 of the License, or 25 the Free Software Foundation, either version 3 of the License, or
26 (at your option) any later version. 26 (at your option) any later version.
27 27
28 GNU Emacs is distributed in the hope that it will be useful, 28 GNU Emacs is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of 29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details. 31 GNU General Public License for more details.
32 32
33 You should have received a copy of the GNU General Public License 33 You should have received a copy of the GNU General Public License
34 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. 34 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 70604238117..5e38629461b 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,16 @@
12011-03-10 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * emacs-lisp/cconv.el (cconv--convert-function): Rename from
4 cconv-closure-convert-function.
5 (cconv-convert): Rename from cconv-closure-convert-rec.
6 (cconv--analyse-use): Rename from cconv-analyse-use.
7 (cconv--analyse-function): Rename from cconv-analyse-function.
8 (cconv--analyse-use): Change some patterns to silence compiler.
9 (cconv-convert, cconv--convert-function): Rewrite.
10
11 * emacs-lisp/byte-opt.el (byte-compile-inline-expand): Adjust check for
12 new byte-code representation.
13
12011-03-06 Stefan Monnier <monnier@iro.umontreal.ca> 142011-03-06 Stefan Monnier <monnier@iro.umontreal.ca>
2 15
3 * emacs-lisp/bytecomp.el (byte-compile-arglist-signature): 16 * emacs-lisp/bytecomp.el (byte-compile-arglist-signature):
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 6d6eb68535e..a49218fe02d 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -287,8 +287,7 @@
287 ;; old-style-byte-codes, but not mixed cases (not sure 287 ;; old-style-byte-codes, but not mixed cases (not sure
288 ;; about new-style into new-style). 288 ;; about new-style into new-style).
289 (not lexical-binding) 289 (not lexical-binding)
290 (not (and (>= (length fn) 7) 290 (not (integerp (aref fn 0)))) ;New lexical byte-code.
291 (aref fn 6)))) ;6 = COMPILED_PUSH_ARGS
292 ;; (message "Inlining %S byte-code" name) 291 ;; (message "Inlining %S byte-code" name)
293 (fetch-bytecode fn) 292 (fetch-bytecode fn)
294 (let ((string (aref fn 1))) 293 (let ((string (aref fn 1)))
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index 5501c13ee4f..741bc7ce74f 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -70,7 +70,6 @@
70;; - maybe unify byte-optimize and compiler-macros. 70;; - maybe unify byte-optimize and compiler-macros.
71;; - canonize code in macro-expand so we don't have to handle (let (var) body) 71;; - canonize code in macro-expand so we don't have to handle (let (var) body)
72;; and other oddities. 72;; and other oddities.
73;; - clean up cconv-closure-convert-rec, especially the `let' binding part.
74;; - new byte codes for unwind-protect, catch, and condition-case so that 73;; - new byte codes for unwind-protect, catch, and condition-case so that
75;; closures aren't needed at all. 74;; closures aren't needed at all.
76;; - a reference to a var that is known statically to always hold a constant 75;; - a reference to a var that is known statically to always hold a constant
@@ -81,6 +80,8 @@
81;; - Since we know here when a variable is not mutated, we could pass that 80;; - Since we know here when a variable is not mutated, we could pass that
82;; info to the byte-compiler, e.g. by using a new `immutable-let'. 81;; info to the byte-compiler, e.g. by using a new `immutable-let'.
83;; - add tail-calls to bytecode.c and the byte compiler. 82;; - add tail-calls to bytecode.c and the byte compiler.
83;; - call known non-escaping functions with gotos rather than `call'.
84;; - optimize mapcar to a while loop.
84 85
85;; (defmacro dlet (binders &rest body) 86;; (defmacro dlet (binders &rest body)
86;; ;; Works in both lexical and non-lexical mode. 87;; ;; Works in both lexical and non-lexical mode.
@@ -142,13 +143,7 @@ Returns a form where all lambdas don't have any free variables."
142 ;; Analyse form - fill these variables with new information. 143 ;; Analyse form - fill these variables with new information.
143 (cconv-analyse-form form '()) 144 (cconv-analyse-form form '())
144 (setq cconv-freevars-alist (nreverse cconv-freevars-alist)) 145 (setq cconv-freevars-alist (nreverse cconv-freevars-alist))
145 (cconv-closure-convert-rec 146 (cconv-convert form nil nil))) ; Env initially empty.
146 form ; the tree
147 '() ;
148 '() ; fvrs initially empty
149 '() ; envs initially empty
150 '()
151 )))
152 147
153(defconst cconv--dummy-var (make-symbol "ignored")) 148(defconst cconv--dummy-var (make-symbol "ignored"))
154 149
@@ -189,71 +184,79 @@ Returns a form where all lambdas don't have any free variables."
189 (unless (memq (car b) s) (push b res))) 184 (unless (memq (car b) s) (push b res)))
190 (nreverse res))) 185 (nreverse res)))
191 186
192(defun cconv-closure-convert-function (fvrs vars emvrs envs lmenvs body-forms 187(defun cconv--convert-function (args body env parentform)
193 parentform) 188 (assert (equal body (caar cconv-freevars-alist)))
194 (assert (equal body-forms (caar cconv-freevars-alist))) 189 (let* ((fvs (cdr (pop cconv-freevars-alist)))
195 (let* ((fvrs-new (cconv--set-diff fvrs vars)) ; Remove vars from fvrs. 190 (body-new '())
196 (fv (cdr (pop cconv-freevars-alist)))
197 (body-forms-new '())
198 (letbind '()) 191 (letbind '())
199 (envector nil)) 192 (envector ())
200 (when fv 193 (i 0)
201 ;; Here we form our environment vector. 194 (new-env ()))
202 195 ;; Build the "formal and actual envs" for the closure-converted function.
203 (dolist (elm fv) 196 (dolist (fv fvs)
204 (push 197 (let ((exp (or (cdr (assq fv env)) fv)))
205 (cconv-closure-convert-rec 198 (pcase exp
206 ;; Remove `elm' from `emvrs' for this call because in case 199 ;; If `fv' is a variable that's wrapped in a cons-cell,
207 ;; `elm' is a variable that's wrapped in a cons-cell, we 200 ;; we want to put the cons-cell itself in the closure,
208 ;; want to put the cons-cell itself in the closure, rather 201 ;; rather than just a copy of its current content.
209 ;; than just a copy of its current content. 202 (`(car ,iexp . ,_)
210 elm (remq elm emvrs) fvrs envs lmenvs) 203 (push iexp envector)
211 envector)) ; Process vars for closure vector. 204 (push `(,fv . (car (internal-get-closed-var ,i))) new-env))
212 (setq envector (reverse envector)) 205 (_
213 (setq envs fv) 206 (push exp envector)
214 (setq fvrs-new fv)) ; Update substitution list. 207 (push `(,fv . (internal-get-closed-var ,i)) new-env))))
215 208 (setq i (1+ i)))
216 (setq emvrs (cconv--set-diff emvrs vars)) 209 (setq envector (nreverse envector))
217 (setq lmenvs (cconv--map-diff-set lmenvs vars)) 210 (setq new-env (nreverse new-env))
218 211
219 ;; The difference between envs and fvrs is explained 212 (dolist (arg args)
220 ;; in comment in the beginning of the function. 213 (if (not (member (cons (list arg) parentform) cconv-captured+mutated))
221 (dolist (var vars) 214 (if (assq arg new-env) (push `(,arg) new-env))
222 (when (member (cons (list var) parentform) cconv-captured+mutated) 215 (push `(,arg . (car ,arg)) new-env)
223 (push var emvrs) 216 (push `(,arg (list ,arg)) letbind)))
224 (push `(,var (list ,var)) letbind))) 217
225 (dolist (elm body-forms) ; convert function body 218 (setq body-new (mapcar (lambda (form)
226 (push (cconv-closure-convert-rec 219 (cconv-convert form new-env nil))
227 elm emvrs fvrs-new envs lmenvs) 220 body))
228 body-forms-new)) 221
229 222 (when letbind
230 (setq body-forms-new 223 (let ((special-forms '()))
231 (if letbind `((let ,letbind . ,(reverse body-forms-new))) 224 ;; Keep special forms at the beginning of the body.
232 (reverse body-forms-new))) 225 (while (or (stringp (car body-new)) ;docstring.
226 (memq (car-safe (car body-new)) '(interactive declare)))
227 (push (pop body-new) special-forms))
228 (setq body-new
229 `(,@(nreverse special-forms) (let ,letbind . ,body-new)))))
233 230
234 (cond 231 (cond
235 ;if no freevars - do nothing 232 ((null envector) ;if no freevars - do nothing
236 ((null envector) 233 `(function (lambda ,args . ,body-new)))
237 `(function (lambda ,vars . ,body-forms-new)))
238 ; 1 free variable - do not build vector
239 (t 234 (t
240 `(internal-make-closure 235 `(internal-make-closure
241 ,vars ,envector . ,body-forms-new))))) 236 ,args ,envector . ,body-new)))))
242 237
243(defun cconv-closure-convert-rec (form emvrs fvrs envs lmenvs) 238(defun cconv-convert (form env extend)
244 ;; This function actually rewrites the tree. 239 ;; This function actually rewrites the tree.
245 "Eliminates all free variables of all lambdas in given forms. 240 "Return FORM with all its lambdas changed so they are closed.
246Arguments: 241ENV is a lexical environment mapping variables to the expression
247- FORM is a piece of Elisp code after macroexpansion. 242used to get its value. This is used for variables that are copied into
248- LMENVS is a list of environments used for lambda-lifting. Initially empty. 243closures, moved into cons cells, ...
249- EMVRS is a list that contains mutated variables that are visible 244ENV is a list where each entry takes the shape either:
250within current environment. 245 (VAR . (car EXP)): VAR has been moved into the car of a cons-cell, and EXP
251- ENVS is an environment(list of free variables) of current closure. 246 is an expression that evaluates to this cons-cell.
252Initially empty. 247 (VAR . (internal-get-closed-var N)): VAR has been copied into the closure
253- FVRS is a list of variables to substitute in each context. 248 environment's Nth slot.
254Initially empty. 249 (VAR . (apply-partially F ARG1 ARG2 ..)): VAR has been λ-lifted and takes
255 250 additional arguments ARGs.
256Returns a form where all lambdas don't have any free variables." 251EXTEND is a list of variables which might need to be accessed even from places
252where they are shadowed, because some part of ENV causes them to be used at
253places where they originally did not directly appear."
254 (assert (not (delq nil (mapcar (lambda (mapping)
255 (if (eq (cadr mapping) 'apply-partially)
256 (cconv--set-diff (cdr (cddr mapping))
257 extend)))
258 env))))
259
257 ;; What's the difference between fvrs and envs? 260 ;; What's the difference between fvrs and envs?
258 ;; Suppose that we have the code 261 ;; Suppose that we have the code
259 ;; (lambda (..) fvr (let ((fvr 1)) (+ fvr 1))) 262 ;; (lambda (..) fvr (let ((fvr 1)) (+ fvr 1)))
@@ -266,18 +269,12 @@ Returns a form where all lambdas don't have any free variables."
266 ;; so we never touch it(unless we enter to the other closure). 269 ;; so we never touch it(unless we enter to the other closure).
267 ;;(if (listp form) (print (car form)) form) 270 ;;(if (listp form) (print (car form)) form)
268 (pcase form 271 (pcase form
269 (`(,(and letsym (or `let* `let)) ,binders . ,body-forms) 272 (`(,(and letsym (or `let* `let)) ,binders . ,body)
270 273
271 ; let and let* special forms 274 ; let and let* special forms
272 (let ((body-forms-new '()) 275 (let ((binders-new '())
273 (binders-new '()) 276 (new-env env)
274 ;; next for variables needed for delayed push 277 (new-extend extend))
275 ;; because we should process <value(s)>
276 ;; before we change any arguments
277 (lmenvs-new '()) ;needed only in case of let
278 (emvrs-new '()) ;needed only in case of let
279 (emvr-push) ;needed only in case of let*
280 (lmenv-push)) ;needed only in case of let*
281 278
282 (dolist (binder binders) 279 (dolist (binder binders)
283 (let* ((value nil) 280 (let* ((value nil)
@@ -288,372 +285,223 @@ Returns a form where all lambdas don't have any free variables."
288 (new-val 285 (new-val
289 (cond 286 (cond
290 ;; Check if var is a candidate for lambda lifting. 287 ;; Check if var is a candidate for lambda lifting.
291 ((member (cons binder form) cconv-lambda-candidates) 288 ((and (member (cons binder form) cconv-lambda-candidates)
292 (assert (and (eq (car value) 'function) 289 (progn
293 (eq (car (cadr value)) 'lambda))) 290 (assert (and (eq (car value) 'function)
294 (assert (equal (cddr (cadr value)) 291 (eq (car (cadr value)) 'lambda)))
295 (caar cconv-freevars-alist))) 292 (assert (equal (cddr (cadr value))
296 ;; Peek at the freevars to decide whether to λ-lift. 293 (caar cconv-freevars-alist)))
297 (let* ((fv (cdr (car cconv-freevars-alist))) 294 ;; Peek at the freevars to decide whether to λ-lift.
298 (funargs (cadr (cadr value))) 295 (let* ((fvs (cdr (car cconv-freevars-alist)))
299 (funcvars (append fv funargs)) 296 (fun (cadr value))
300 (funcbodies (cddadr value)) ; function bodies 297 (funargs (cadr fun))
301 (funcbodies-new '())) 298 (funcvars (append fvs funargs)))
302 ; lambda lifting condition 299 ; lambda lifting condition
303 (if (or (not fv) (< cconv-liftwhen (length funcvars))) 300 (and fvs (>= cconv-liftwhen (length funcvars))))))
304 ; do not lift 301 ; Lift.
305 (progn 302 (let* ((fvs (cdr (pop cconv-freevars-alist)))
306 ;; (byte-compile-log-warning 303 (fun (cadr value))
307 ;; (format "Not λ-lifting `%S': %d > %d" 304 (funargs (cadr fun))
308 ;; var (length funcvars) cconv-liftwhen)) 305 (funcvars (append fvs funargs))
309 306 (funcbody (cddr fun))
310 (cconv-closure-convert-rec 307 (funcbody-env ()))
311 value emvrs fvrs envs lmenvs)) 308 (push `(,var . (apply-partially ,var . ,fvs)) new-env)
312 ; lift 309 (dolist (fv fvs)
313 (progn 310 (pushnew fv new-extend)
314 ;; (byte-compile-log-warning 311 (if (and (eq 'car (car-safe (cdr (assq fv env))))
315 ;; (format "λ-lifting `%S'" var)) 312 (not (memq fv funargs)))
316 (setq cconv-freevars-alist 313 (push `(,fv . (car ,fv)) funcbody-env)))
317 ;; Now that we know we'll λ-lift, consume the 314 `(function (lambda ,funcvars .
318 ;; freevar data. 315 ,(mapcar (lambda (form)
319 (cdr cconv-freevars-alist)) 316 (cconv-convert
320 (dolist (elm2 funcbodies) 317 form funcbody-env nil))
321 (push ; convert function bodies 318 funcbody)))))
322 (cconv-closure-convert-rec
323 elm2 emvrs nil envs lmenvs)
324 funcbodies-new))
325 (if (eq letsym 'let*)
326 (setq lmenv-push (cons var fv))
327 (push (cons var fv) lmenvs-new))
328 ; push lifted function
329
330 `(function .
331 ((lambda ,funcvars .
332 ,(reverse funcbodies-new))))))))
333 319
334 ;; Check if it needs to be turned into a "ref-cell". 320 ;; Check if it needs to be turned into a "ref-cell".
335 ((member (cons binder form) cconv-captured+mutated) 321 ((member (cons binder form) cconv-captured+mutated)
336 ;; Declared variable is mutated and captured. 322 ;; Declared variable is mutated and captured.
337 (prog1 323 (push `(,var . (car ,var)) new-env)
338 `(list ,(cconv-closure-convert-rec 324 `(list ,(cconv-convert value env extend)))
339 value emvrs
340 fvrs envs lmenvs))
341 (if (eq letsym 'let*)
342 (setq emvr-push var)
343 (push var emvrs-new))))
344 325
345 ;; Normal default case. 326 ;; Normal default case.
346 (t 327 (t
347 (cconv-closure-convert-rec 328 (if (assq var new-env) (push `(,var) new-env))
348 value emvrs fvrs envs lmenvs))))) 329 (cconv-convert value env extend)))))
349 330
350 ;; this piece of code below letbinds free 331 ;; The piece of code below letbinds free variables of a λ-lifted
351 ;; variables of a lambda lifted function 332 ;; function if they are redefined in this let, example:
352 ;; if they are redefined in this let 333 ;; (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1))
353 ;; example: 334 ;; Here we can not pass y as parameter because it is redefined.
354 ;; (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1)) 335 ;; So we add a (closed-y y) declaration. We do that even if the
355 ;; Here we can not pass y as parameter because it is 336 ;; function is not used inside this let(*). The reason why we
356 ;; redefined. We add a (closed-y y) declaration. 337 ;; ignore this case is that we can't "look forward" to see if the
357 ;; We do that even if the function is not used inside 338 ;; function is called there or not. To treat this case better we'd
358 ;; this let(*). The reason why we ignore this case is 339 ;; need to traverse the tree one more time to collect this data, and
359 ;; that we can't "look forward" to see if the function 340 ;; I think that it's not worth it.
360 ;; is called there or not. To treat well this case we 341 (when (memq var new-extend)
361 ;; need to traverse the tree one more time to collect this 342 (let ((closedsym
362 ;; data, and I think that it's not worth it. 343 (make-symbol (concat "closed-" (symbol-name var)))))
344 (setq new-env
345 (mapcar (lambda (mapping)
346 (if (not (eq (cadr mapping) 'apply-partially))
347 mapping
348 (assert (eq (car mapping) (nth 2 mapping)))
349 (list* (car mapping)
350 'apply-partially
351 (car mapping)
352 (mapcar (lambda (arg)
353 (if (eq var arg)
354 closedsym arg))
355 (nthcdr 3 mapping)))))
356 new-env))
357 (setq new-extend (remq var new-extend))
358 (push closedsym new-extend)
359 (push `(,closedsym ,var) binders-new)))
363 360
364 (when (eq letsym 'let*)
365 (let ((closedsym '())
366 (new-lmenv '())
367 (old-lmenv '()))
368 (dolist (lmenv lmenvs)
369 (when (memq var (cdr lmenv))
370 (setq closedsym
371 (make-symbol
372 (concat "closed-" (symbol-name var))))
373 (setq new-lmenv (list (car lmenv)))
374 (dolist (frv (cdr lmenv)) (if (eq frv var)
375 (push closedsym new-lmenv)
376 (push frv new-lmenv)))
377 (setq new-lmenv (reverse new-lmenv))
378 (setq old-lmenv lmenv)))
379 (when new-lmenv
380 (setq lmenvs (remq old-lmenv lmenvs))
381 (push new-lmenv lmenvs)
382 (push `(,closedsym ,var) binders-new))))
383 ;; We push the element after redefined free variables are 361 ;; We push the element after redefined free variables are
384 ;; processed. This is important to avoid the bug when free 362 ;; processed. This is important to avoid the bug when free
385 ;; variable and the function have the same name. 363 ;; variable and the function have the same name.
386 (push (list var new-val) binders-new) 364 (push (list var new-val) binders-new)
387 365
388 (when (eq letsym 'let*) ; update fvrs 366 (when (eq letsym 'let*)
389 (setq fvrs (remq var fvrs)) 367 (setq env new-env)
390 (setq emvrs (remq var emvrs)) ; remove if redefined 368 (setq extend new-extend))
391 (when emvr-push 369 )) ; end of dolist over binders
392 (push emvr-push emvrs) 370
393 (setq emvr-push nil)) 371 `(,letsym ,(nreverse binders-new)
394 (setq lmenvs (cconv--map-diff-elem lmenvs var)) 372 . ,(mapcar (lambda (form)
395 (when lmenv-push 373 (cconv-convert
396 (push lmenv-push lmenvs) 374 form new-env new-extend))
397 (setq lmenv-push nil))) 375 body))))
398 )) ; end of dolist over binders
399 (when (eq letsym 'let)
400
401 ;; Here we update emvrs, fvrs and lmenvs lists
402 (setq fvrs (cconv--set-diff-map fvrs binders-new))
403 (setq emvrs (cconv--set-diff-map emvrs binders-new))
404 (setq emvrs (append emvrs emvrs-new))
405 (setq lmenvs (cconv--set-diff-map lmenvs binders-new))
406 (setq lmenvs (append lmenvs lmenvs-new))
407
408 ;; Here we do the same letbinding as for let* above
409 ;; to avoid situation when a free variable of a lambda lifted
410 ;; function got redefined.
411
412 (let ((new-lmenv)
413 (var nil)
414 (closedsym nil)
415 (letbinds '()))
416 (dolist (binder binders)
417 (setq var (if (consp binder) (car binder) binder))
418
419 (let ((lmenvs-1 lmenvs)) ; just to avoid manipulating
420 (dolist (lmenv lmenvs-1) ; the counter inside the loop
421 (when (memq var (cdr lmenv))
422 (setq closedsym (make-symbol
423 (concat "closed-"
424 (symbol-name var))))
425
426 (setq new-lmenv (list (car lmenv)))
427 (dolist (frv (cdr lmenv))
428 (push (if (eq frv var) closedsym frv)
429 new-lmenv))
430 (setq new-lmenv (reverse new-lmenv))
431 (setq lmenvs (remq lmenv lmenvs))
432 (push new-lmenv lmenvs)
433 (push `(,closedsym ,var) letbinds)
434 ))))
435 (setq binders-new (append binders-new letbinds))))
436
437 (dolist (elm body-forms) ; convert body forms
438 (push (cconv-closure-convert-rec
439 elm emvrs fvrs envs lmenvs)
440 body-forms-new))
441 `(,letsym ,(reverse binders-new) . ,(reverse body-forms-new))))
442 ;end of let let* forms 376 ;end of let let* forms
443 377
444 ; first element is lambda expression 378 ; first element is lambda expression
445 (`(,(and `(lambda . ,_) fun) . ,other-body-forms) 379 (`(,(and `(lambda . ,_) fun) . ,args)
446 380 ;; FIXME: it's silly to create a closure just to call it.
447 (let ((other-body-forms-new '())) 381 `(funcall
448 (dolist (elm other-body-forms) 382 ,(cconv-convert `(function ,fun) env extend)
449 (push (cconv-closure-convert-rec 383 ,@(mapcar (lambda (form)
450 elm emvrs fvrs envs lmenvs) 384 (cconv-convert form env extend))
451 other-body-forms-new)) 385 args)))
452 `(funcall
453 ,(cconv-closure-convert-rec
454 (list 'function fun) emvrs fvrs envs lmenvs)
455 ,@(nreverse other-body-forms-new))))
456 386
457 (`(cond . ,cond-forms) ; cond special form 387 (`(cond . ,cond-forms) ; cond special form
458 (let ((cond-forms-new '())) 388 `(cond . ,(mapcar (lambda (branch)
459 (dolist (elm cond-forms) 389 (mapcar (lambda (form)
460 (push (let ((elm-new '())) 390 (cconv-convert form env extend))
461 (dolist (elm-2 elm) 391 branch))
462 (push 392 cond-forms)))
463 (cconv-closure-convert-rec
464 elm-2 emvrs fvrs envs lmenvs)
465 elm-new))
466 (reverse elm-new))
467 cond-forms-new))
468 (cons 'cond
469 (reverse cond-forms-new))))
470
471 (`(quote . ,_) form)
472 393
473 (`(function (lambda ,vars . ,body-forms)) ; function form 394 (`(function (lambda ,args . ,body) . ,_)
474 (cconv-closure-convert-function 395 (cconv--convert-function args body env form))
475 fvrs vars emvrs envs lmenvs body-forms form))
476 396
477 (`(internal-make-closure . ,_) 397 (`(internal-make-closure . ,_)
478 (error "Internal byte-compiler error: cconv called twice")) 398 (byte-compile-report-error
399 "Internal error in compiler: cconv called twice?"))
479 400
480 (`(function . ,_) form) ; Same as quote. 401 (`(quote . ,_) form)
402 (`(function . ,_) form)
481 403
482 ;defconst, defvar 404 ;defconst, defvar
483 (`(,(and sym (or `defconst `defvar)) ,definedsymbol . ,body-forms) 405 (`(,(and sym (or `defconst `defvar)) ,definedsymbol . ,forms)
484 406 `(,sym ,definedsymbol
485 (let ((body-forms-new '())) 407 . ,(mapcar (lambda (form) (cconv-convert form env extend))
486 (dolist (elm body-forms) 408 forms)))
487 (push (cconv-closure-convert-rec
488 elm emvrs fvrs envs lmenvs)
489 body-forms-new))
490 (setq body-forms-new (reverse body-forms-new))
491 `(,sym ,definedsymbol . ,body-forms-new)))
492 409
493 ;defun, defmacro 410 ;defun, defmacro
494 (`(,(and sym (or `defun `defmacro)) 411 (`(,(and sym (or `defun `defmacro))
495 ,func ,vars . ,body-forms) 412 ,func ,args . ,body)
496 413 (assert (equal body (caar cconv-freevars-alist)))
497 ;; The freevar data was pushed onto cconv-freevars-alist
498 ;; but we don't need it.
499 (assert (equal body-forms (caar cconv-freevars-alist)))
500 (assert (null (cdar cconv-freevars-alist))) 414 (assert (null (cdar cconv-freevars-alist)))
501 (setq cconv-freevars-alist (cdr cconv-freevars-alist)) 415
502 416 (let ((new (cconv--convert-function args body env form)))
503 (let ((body-new '()) ; The whole body. 417 (pcase new
504 (body-forms-new '()) ; Body w\o docstring and interactive. 418 (`(function (lambda ,newargs . ,new-body))
505 (letbind '())) 419 (assert (equal args newargs))
506 ; Find mutable arguments. 420 `(,sym ,func ,args . ,new-body))
507 (dolist (elm vars) 421 (t (byte-compile-report-error
508 (when (member (cons (list elm) form) cconv-captured+mutated) 422 (format "Internal error in cconv of (%s %s ...)" sym func))))))
509 (push elm letbind)
510 (push elm emvrs)))
511 ;Transform body-forms.
512 (when (stringp (car body-forms)) ; Treat docstring well.
513 (push (car body-forms) body-new)
514 (setq body-forms (cdr body-forms)))
515 (when (eq (car-safe (car body-forms)) 'interactive)
516 (push (cconv-closure-convert-rec
517 (car body-forms)
518 emvrs fvrs envs lmenvs)
519 body-new)
520 (setq body-forms (cdr body-forms)))
521
522 (dolist (elm body-forms)
523 (push (cconv-closure-convert-rec
524 elm emvrs fvrs envs lmenvs)
525 body-forms-new))
526 (setq body-forms-new (reverse body-forms-new))
527
528 (if letbind
529 ; Letbind mutable arguments.
530 (let ((binders-new '()))
531 (dolist (elm letbind) (push `(,elm (list ,elm))
532 binders-new))
533 (push `(let ,(reverse binders-new) .
534 ,body-forms-new) body-new)
535 (setq body-new (reverse body-new)))
536 (setq body-new (append (reverse body-new) body-forms-new)))
537
538 `(,sym ,func ,vars . ,body-new)))
539 423
540 ;condition-case 424 ;condition-case
541 (`(condition-case ,var ,protected-form . ,handlers) 425 (`(condition-case ,var ,protected-form . ,handlers)
542 (let ((newform (cconv-closure-convert-rec 426 (let ((newform (cconv--convert-function
543 `(function (lambda () ,protected-form)) 427 () (list protected-form) env form)))
544 emvrs fvrs envs lmenvs)))
545 (setq fvrs (remq var fvrs))
546 `(condition-case :fun-body ,newform 428 `(condition-case :fun-body ,newform
547 ,@(mapcar (lambda (handler) 429 ,@(mapcar (lambda (handler)
548 (list (car handler) 430 (list (car handler)
549 (cconv-closure-convert-rec 431 (cconv--convert-function
550 (let ((arg (or var cconv--dummy-var))) 432 (list (or var cconv--dummy-var))
551 `(function (lambda (,arg) ,@(cdr handler)))) 433 (cdr handler) env form)))
552 emvrs fvrs envs lmenvs)))
553 handlers)))) 434 handlers))))
554 435
555 (`(,(and head (or `catch `unwind-protect)) ,form . ,body) 436 (`(,(and head (or `catch `unwind-protect)) ,form . ,body)
556 `(,head ,(cconv-closure-convert-rec form emvrs fvrs envs lmenvs) 437 `(,head ,(cconv-convert form env extend)
557 :fun-body 438 :fun-body ,(cconv--convert-function () body env form)))
558 ,(cconv-closure-convert-rec `(function (lambda () ,@body))
559 emvrs fvrs envs lmenvs)))
560 439
561 (`(track-mouse . ,body) 440 (`(track-mouse . ,body)
562 `(track-mouse 441 `(track-mouse
563 :fun-body 442 :fun-body ,(cconv--convert-function () body env form)))
564 ,(cconv-closure-convert-rec `(function (lambda () ,@body))
565 emvrs fvrs envs lmenvs)))
566 443
567 (`(setq . ,forms) ; setq special form 444 (`(setq . ,forms) ; setq special form
568 (let (prognlist sym sym-new value) 445 (let ((prognlist ()))
569 (while forms 446 (while forms
570 (setq sym (car forms)) 447 (let* ((sym (pop forms))
571 (setq sym-new (cconv-closure-convert-rec 448 (sym-new (or (cdr (assq sym env)) sym))
572 sym 449 (value (cconv-convert (pop forms) env extend)))
573 (remq sym emvrs) fvrs envs lmenvs)) 450 (push (pcase sym-new
574 (setq value 451 ((pred symbolp) `(setq ,sym-new ,value))
575 (cconv-closure-convert-rec 452 (`(car ,iexp) `(setcar ,iexp ,value))
576 (cadr forms) emvrs fvrs envs lmenvs)) 453 ;; This "should never happen", but for variables which are
577 (cond 454 ;; mutated+captured+unused, we may end up trying to `setq'
578 ((memq sym emvrs) (push `(setcar ,sym-new ,value) prognlist)) 455 ;; on a closed-over variable, so just drop the setq.
579 ((symbolp sym-new) (push `(setq ,sym-new ,value) prognlist)) 456 (_ ;; (byte-compile-report-error
580 ;; This should never happen, but for variables which are 457 ;; (format "Internal error in cconv of (setq %s ..)"
581 ;; mutated+captured+unused, we may end up trying to `setq' 458 ;; sym-new))
582 ;; on a closed-over variable, so just drop the setq. 459 value))
583 (t (push value prognlist))) 460 prognlist)))
584 (setq forms (cddr forms)))
585 (if (cdr prognlist) 461 (if (cdr prognlist)
586 `(progn . ,(reverse prognlist)) 462 `(progn . ,(nreverse prognlist))
587 (car prognlist)))) 463 (car prognlist))))
588 464
589 (`(,(and (or `funcall `apply) callsym) ,fun . ,args) 465 (`(,(and (or `funcall `apply) callsym) ,fun . ,args)
590 ; funcall is not a special form 466 ;; These are not special forms but we treat them separately for the needs
591 ; but we treat it separately 467 ;; of lambda lifting.
592 ; for the needs of lambda lifting 468 (let ((mapping (cdr (assq fun env))))
593 (let ((fv (cdr (assq fun lmenvs)))) 469 (pcase mapping
594 (if fv 470 (`(apply-partially ,_ . ,(and fvs `(,_ . ,_)))
595 (let ((args-new '()) 471 (assert (eq (cadr mapping) fun))
596 (processed-fv '())) 472 `(,callsym ,fun
597 ;; All args (free variables and actual arguments) 473 ,@(mapcar (lambda (fv)
598 ;; should be processed, because they can be fvrs 474 (let ((exp (or (cdr (assq fv env)) fv)))
599 ;; (free variables of another closure) 475 (pcase exp
600 (dolist (fvr fv) 476 (`(car ,iexp . ,_) iexp)
601 (push (cconv-closure-convert-rec 477 (_ exp))))
602 fvr (remq fvr emvrs) 478 fvs)
603 fvrs envs lmenvs) 479 ,@(mapcar (lambda (arg)
604 processed-fv)) 480 (cconv-convert arg env extend))
605 (setq processed-fv (reverse processed-fv)) 481 args)))
606 (dolist (elm args) 482 (_ `(,callsym ,@(mapcar (lambda (arg)
607 (push (cconv-closure-convert-rec 483 (cconv-convert arg env extend))
608 elm emvrs fvrs envs lmenvs) 484 (cons fun args)))))))
609 args-new))
610 (setq args-new (append processed-fv (reverse args-new)))
611 (setq fun (cconv-closure-convert-rec
612 fun emvrs fvrs envs lmenvs))
613 `(,callsym ,fun . ,args-new))
614 (let ((cdr-new '()))
615 (dolist (elm (cdr form))
616 (push (cconv-closure-convert-rec
617 elm emvrs fvrs envs lmenvs)
618 cdr-new))
619 `(,callsym . ,(reverse cdr-new))))))
620 485
621 (`(interactive . ,forms) 486 (`(interactive . ,forms)
622 `(interactive 487 `(interactive . ,(mapcar (lambda (form)
623 ,@(mapcar (lambda (form) 488 (cconv-convert form nil nil))
624 (cconv-closure-convert-rec form nil nil nil nil)) 489 forms)))
625 forms)))
626 490
627 (`(,func . ,body-forms) ; first element is function or whatever 491 (`(,func . ,forms)
628 ; function-like forms are: 492 ;; First element is function or whatever function-like forms are: or, and,
629 ; or, and, if, progn, prog1, prog2, 493 ;; if, progn, prog1, prog2, while, until
630 ; while, until 494 `(,func . ,(mapcar (lambda (form)
631 (let ((body-forms-new '())) 495 (cconv-convert form env extend))
632 (dolist (elm body-forms) 496 forms)))
633 (push (cconv-closure-convert-rec 497
634 elm emvrs fvrs envs lmenvs) 498 (_ (or (cdr (assq form env)) form))))
635 body-forms-new))
636 (setq body-forms-new (reverse body-forms-new))
637 `(,func . ,body-forms-new)))
638
639 (_
640 (let ((free (memq form fvrs)))
641 (if free ;form is a free variable
642 (let* ((numero (- (length fvrs) (length free)))
643 ;; Replace form => (aref env #)
644 (var `(internal-get-closed-var ,numero)))
645 (if (memq form emvrs) ; form => (car (aref env #)) if mutable
646 `(car ,var)
647 var))
648 (if (memq form emvrs) ; if form is a mutable variable
649 `(car ,form) ; replace form => (car form)
650 form))))))
651 499
652(unless (fboundp 'byte-compile-not-lexical-var-p) 500(unless (fboundp 'byte-compile-not-lexical-var-p)
653 ;; Only used to test the code in non-lexbind Emacs. 501 ;; Only used to test the code in non-lexbind Emacs.
654 (defalias 'byte-compile-not-lexical-var-p 'boundp)) 502 (defalias 'byte-compile-not-lexical-var-p 'boundp))
655 503
656(defun cconv-analyse-use (vardata form varkind) 504(defun cconv--analyse-use (vardata form varkind)
657 "Analyse the use of a variable. 505 "Analyse the use of a variable.
658VARDATA should be (BINDER READ MUTATED CAPTURED CALLED). 506VARDATA should be (BINDER READ MUTATED CAPTURED CALLED).
659VARKIND is the name of the kind of variable. 507VARKIND is the name of the kind of variable.
@@ -663,8 +511,8 @@ FORM is the parent form that binds this var."
663 (`(,_ nil nil nil nil) nil) 511 (`(,_ nil nil nil nil) nil)
664 (`((,(and (pred (lambda (var) (eq ?_ (aref (symbol-name var) 0)))) var) . ,_) 512 (`((,(and (pred (lambda (var) (eq ?_ (aref (symbol-name var) 0)))) var) . ,_)
665 ,_ ,_ ,_ ,_) 513 ,_ ,_ ,_ ,_)
666 (byte-compile-log-warning (format "%s `%S' not left unused" varkind var))) 514 (byte-compile-log-warning
667 ((or `(,_ ,_ ,_ ,_ ,_) dontcare) nil)) 515 (format "%s `%S' not left unused" varkind var))))
668 (pcase vardata 516 (pcase vardata
669 (`((,var . ,_) nil ,_ ,_ nil) 517 (`((,var . ,_) nil ,_ ,_ nil)
670 ;; FIXME: This gives warnings in the wrong order, with imprecise line 518 ;; FIXME: This gives warnings in the wrong order, with imprecise line
@@ -681,11 +529,9 @@ FORM is the parent form that binds this var."
681 (`(,binder ,_ t t ,_) 529 (`(,binder ,_ t t ,_)
682 (push (cons binder form) cconv-captured+mutated)) 530 (push (cons binder form) cconv-captured+mutated))
683 (`(,(and binder `(,_ (function (lambda . ,_)))) nil nil nil t) 531 (`(,(and binder `(,_ (function (lambda . ,_)))) nil nil nil t)
684 (push (cons binder form) cconv-lambda-candidates)) 532 (push (cons binder form) cconv-lambda-candidates))))
685 (`(,_ ,_ ,_ ,_ ,_) nil)
686 (dontcare)))
687 533
688(defun cconv-analyse-function (args body env parentform) 534(defun cconv--analyse-function (args body env parentform)
689 (let* ((newvars nil) 535 (let* ((newvars nil)
690 (freevars (list body)) 536 (freevars (list body))
691 ;; We analyze the body within a new environment where all uses are 537 ;; We analyze the body within a new environment where all uses are
@@ -710,7 +556,7 @@ FORM is the parent form that binds this var."
710 (cconv-analyse-form form newenv)) 556 (cconv-analyse-form form newenv))
711 ;; Summarize resulting data about arguments. 557 ;; Summarize resulting data about arguments.
712 (dolist (vardata newvars) 558 (dolist (vardata newvars)
713 (cconv-analyse-use vardata parentform "argument")) 559 (cconv--analyse-use vardata parentform "argument"))
714 ;; Transfer uses collected in `envcopy' (via `newenv') back to `env'; 560 ;; Transfer uses collected in `envcopy' (via `newenv') back to `env';
715 ;; and compute free variables. 561 ;; and compute free variables.
716 (while env 562 (while env
@@ -763,7 +609,7 @@ and updates the data stored in ENV."
763 (cconv-analyse-form form env)) 609 (cconv-analyse-form form env))
764 610
765 (dolist (vardata newvars) 611 (dolist (vardata newvars)
766 (cconv-analyse-use vardata form "variable")))) 612 (cconv--analyse-use vardata form "variable"))))
767 613
768 ; defun special form 614 ; defun special form
769 (`(,(or `defun `defmacro) ,func ,vrs . ,body-forms) 615 (`(,(or `defun `defmacro) ,func ,vrs . ,body-forms)
@@ -772,10 +618,10 @@ and updates the data stored in ENV."
772 (format "Function %S will ignore its context %S" 618 (format "Function %S will ignore its context %S"
773 func (mapcar #'car env)) 619 func (mapcar #'car env))
774 t :warning)) 620 t :warning))
775 (cconv-analyse-function vrs body-forms nil form)) 621 (cconv--analyse-function vrs body-forms nil form))
776 622
777 (`(function (lambda ,vrs . ,body-forms)) 623 (`(function (lambda ,vrs . ,body-forms))
778 (cconv-analyse-function vrs body-forms env form)) 624 (cconv--analyse-function vrs body-forms env form))
779 625
780 (`(setq . ,forms) 626 (`(setq . ,forms)
781 ;; If a local variable (member of env) is modified by setq then 627 ;; If a local variable (member of env) is modified by setq then
@@ -801,19 +647,19 @@ and updates the data stored in ENV."
801 ;; FIXME: The bytecode for condition-case forces us to wrap the 647 ;; FIXME: The bytecode for condition-case forces us to wrap the
802 ;; form and handlers in closures (for handlers, it's probably 648 ;; form and handlers in closures (for handlers, it's probably
803 ;; unavoidable, but not for the protected form). 649 ;; unavoidable, but not for the protected form).
804 (cconv-analyse-function () (list protected-form) env form) 650 (cconv--analyse-function () (list protected-form) env form)
805 (dolist (handler handlers) 651 (dolist (handler handlers)
806 (cconv-analyse-function (if var (list var)) (cdr handler) env form))) 652 (cconv--analyse-function (if var (list var)) (cdr handler) env form)))
807 653
808 ;; FIXME: The bytecode for catch forces us to wrap the body. 654 ;; FIXME: The bytecode for catch forces us to wrap the body.
809 (`(,(or `catch `unwind-protect) ,form . ,body) 655 (`(,(or `catch `unwind-protect) ,form . ,body)
810 (cconv-analyse-form form env) 656 (cconv-analyse-form form env)
811 (cconv-analyse-function () body env form)) 657 (cconv--analyse-function () body env form))
812 658
813 ;; FIXME: The bytecode for save-window-excursion and the lack of 659 ;; FIXME: The bytecode for save-window-excursion and the lack of
814 ;; bytecode for track-mouse forces us to wrap the body. 660 ;; bytecode for track-mouse forces us to wrap the body.
815 (`(track-mouse . ,body) 661 (`(track-mouse . ,body)
816 (cconv-analyse-function () body env form)) 662 (cconv--analyse-function () body env form))
817 663
818 (`(,(or `defconst `defvar) ,var ,value . ,_) 664 (`(,(or `defconst `defvar) ,var ,value . ,_)
819 (push var byte-compile-bound-variables) 665 (push var byte-compile-bound-variables)
diff --git a/test/ChangeLog b/test/ChangeLog
index b247b88bc94..dc9b87adfac 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,7 @@
12011-03-10 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * automated/lexbind-tests.el: New file.
4
12011-03-05 Glenn Morris <rgm@gnu.org> 52011-03-05 Glenn Morris <rgm@gnu.org>
2 6
3 * eshell.el: Move here from lisp/eshell/esh-test.el. 7 * eshell.el: Move here from lisp/eshell/esh-test.el.
diff --git a/test/automated/lexbind-tests.el b/test/automated/lexbind-tests.el
new file mode 100644
index 00000000000..1ff31e2422d
--- /dev/null
+++ b/test/automated/lexbind-tests.el
@@ -0,0 +1,75 @@
1;;; lexbind-tests.el --- Testing the lexbind byte-compiler
2
3;; Copyright (C) 2011 Free Software Foundation, Inc.
4
5;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
6;; Keywords:
7
8;; This program is free software; you can redistribute it and/or modify
9;; it under the terms of the GNU General Public License as published by
10;; the Free Software Foundation, either version 3 of the License, or
11;; (at your option) any later version.
12
13;; This program is distributed in the hope that it will be useful,
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;; GNU General Public License for more details.
17
18;; You should have received a copy of the GNU General Public License
19;; along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21;;; Commentary:
22
23;;
24
25;;; Code:
26
27(require 'ert)
28
29(defconst lexbind-tests
30 `(
31 (let ((f #'car))
32 (let ((f (lambda (x) (cons (funcall f x) (cdr x)))))
33 (funcall f '(1 . 2))))
34 )
35 "List of expression for test.
36Each element will be executed by interpreter and with
37bytecompiled code, and their results compared.")
38
39
40
41(defun lexbind-check-1 (pat)
42 "Return non-nil if PAT is the same whether directly evalled or compiled."
43 (let ((warning-minimum-log-level :emergency)
44 (byte-compile-warnings nil)
45 (v0 (condition-case nil
46 (eval pat t)
47 (error nil)))
48 (v1 (condition-case nil
49 (funcall (let ((lexical-binding t))
50 (byte-compile `(lambda nil ,pat))))
51 (error nil))))
52 (equal v0 v1)))
53
54(put 'lexbind-check-1 'ert-explainer 'lexbind-explain-1)
55
56(defun lexbind-explain-1 (pat)
57 (let ((v0 (condition-case nil
58 (eval pat t)
59 (error nil)))
60 (v1 (condition-case nil
61 (funcall (let ((lexical-binding t))
62 (byte-compile (list 'lambda nil pat))))
63 (error nil))))
64 (format "Expression `%s' gives `%s' if directly evalled, `%s' if compiled."
65 pat v0 v1)))
66
67(ert-deftest lexbind-tests ()
68 "Test the Emacs byte compiler lexbind handling."
69 (dolist (pat lexbind-tests)
70 (should (lexbind-check-1 pat))))
71
72
73
74(provide 'lexbind-tests)
75;;; lexbind-tests.el ends here