diff options
| author | Stefan Monnier | 2011-03-09 22:48:44 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2011-03-09 22:48:44 -0500 |
| commit | 6c075cd7c07d8f7f2ae52ab4369e709d7664043e (patch) | |
| tree | 6b3defb08f7f0cb78f48d7fed4a7ef55d09426bc | |
| parent | 0d6459dfb52188481bfd6bb53f1b2f653ecd6a5d (diff) | |
| download | emacs-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/ChangeLog | 68 | ||||
| -rw-r--r-- | lisp/ChangeLog | 13 | ||||
| -rw-r--r-- | lisp/emacs-lisp/byte-opt.el | 3 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cconv.el | 646 | ||||
| -rw-r--r-- | test/ChangeLog | 4 | ||||
| -rw-r--r-- | test/automated/lexbind-tests.el | 75 |
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 @@ | |||
| 1 | 2011-03-01 Stefan Monnier <monnier@iro.umontreal.ca> | 1 | 2011-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 | ||
| 7 | 2011-02-25 Stefan Monnier <monnier@iro.umontreal.ca> | 7 | 2011-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 @@ | |||
| 1 | 2011-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 | |||
| 1 | 2011-03-06 Stefan Monnier <monnier@iro.umontreal.ca> | 14 | 2011-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. |
| 246 | Arguments: | 241 | ENV is a lexical environment mapping variables to the expression |
| 247 | - FORM is a piece of Elisp code after macroexpansion. | 242 | used 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. | 243 | closures, moved into cons cells, ... |
| 249 | - EMVRS is a list that contains mutated variables that are visible | 244 | ENV is a list where each entry takes the shape either: |
| 250 | within 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. |
| 252 | Initially 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. |
| 254 | Initially empty. | 249 | (VAR . (apply-partially F ARG1 ARG2 ..)): VAR has been λ-lifted and takes |
| 255 | 250 | additional arguments ARGs. | |
| 256 | Returns a form where all lambdas don't have any free variables." | 251 | EXTEND is a list of variables which might need to be accessed even from places |
| 252 | where they are shadowed, because some part of ENV causes them to be used at | ||
| 253 | places 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. |
| 658 | VARDATA should be (BINDER READ MUTATED CAPTURED CALLED). | 506 | VARDATA should be (BINDER READ MUTATED CAPTURED CALLED). |
| 659 | VARKIND is the name of the kind of variable. | 507 | VARKIND 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 @@ | |||
| 1 | 2011-03-10 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * automated/lexbind-tests.el: New file. | ||
| 4 | |||
| 1 | 2011-03-05 Glenn Morris <rgm@gnu.org> | 5 | 2011-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. | ||
| 36 | Each element will be executed by interpreter and with | ||
| 37 | bytecompiled 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 | ||