diff options
| author | Stefan Monnier | 2011-02-11 14:48:54 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2011-02-11 14:48:54 -0500 |
| commit | 43e67019dfc4fb7d3474e0fbedcfec60f2300521 (patch) | |
| tree | cf67296a599964c895e443024fe4544bcd54f428 | |
| parent | d779e73c22ae9fedcf6edc6ec286f19cf2e3d89a (diff) | |
| download | emacs-43e67019dfc4fb7d3474e0fbedcfec60f2300521.tar.gz emacs-43e67019dfc4fb7d3474e0fbedcfec60f2300521.zip | |
Make cconv-analyse understand the need for closures.
* lisp/emacs-lisp/byte-lexbind.el (byte-compile-lforminfo-analyze):
Understand the :fun-body case for catch, save-window-excursion, and
condition-case.
(byte-compile-maybe-push-heap-environment): No need when nclosures is
zero and byte-compile-current-num-closures is -1.
* lisp/emacs-lisp/bytecomp.el (byte-compile-lambda): Fix `fun' that was not
renamed to `bytecomp-fun'.
* lisp/emacs-lisp/cconv.el (cconv-not-lexical-var-p): New function.
(cconv-freevars): Use it.
(cconv-closure-convert-rec): Avoid `position'.
(cconv-analyse-function): New function.
(cconv-analyse-form): Use it. `inclosure' can't be nil any more.
Check lexical vars at let-binding time rather than when referenced.
For defuns to be in an empty environment and lambdas to take lexical args.
Pay attention to the need to build closures in catch, unwind-protect,
save-window-excursion, condition-case, and track-mouse.
Fix defconst/defvar handling.
| -rw-r--r-- | lisp/ChangeLog | 22 | ||||
| -rw-r--r-- | lisp/emacs-lisp/byte-lexbind.el | 18 | ||||
| -rw-r--r-- | lisp/emacs-lisp/bytecomp.el | 4 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cconv.el | 362 | ||||
| -rw-r--r-- | lisp/emacs-lisp/macroexp.el | 13 |
5 files changed, 214 insertions, 205 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 7c920b2eadc..6a47a2626a5 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,25 @@ | |||
| 1 | 2011-02-11 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * emacs-lisp/bytecomp.el (byte-compile-lambda): Fix `fun' that was not | ||
| 4 | renamed to `bytecomp-fun'. | ||
| 5 | |||
| 6 | * emacs-lisp/byte-lexbind.el (byte-compile-lforminfo-analyze): | ||
| 7 | Understand the :fun-body case for catch, save-window-excursion, and | ||
| 8 | condition-case. | ||
| 9 | (byte-compile-maybe-push-heap-environment): No need when nclosures is | ||
| 10 | zero and byte-compile-current-num-closures is -1. | ||
| 11 | |||
| 12 | * emacs-lisp/cconv.el (cconv-not-lexical-var-p): New function. | ||
| 13 | (cconv-freevars): Use it. | ||
| 14 | (cconv-closure-convert-rec): Avoid `position'. | ||
| 15 | (cconv-analyse-function): New function. | ||
| 16 | (cconv-analyse-form): Use it. `inclosure' can't be nil any more. | ||
| 17 | Check lexical vars at let-binding time rather than when referenced. | ||
| 18 | For defuns to be in an empty environment and lambdas to take lexical args. | ||
| 19 | Pay attention to the need to build closures in catch, unwind-protect, | ||
| 20 | save-window-excursion, condition-case, and track-mouse. | ||
| 21 | Fix defconst/defvar handling. | ||
| 22 | |||
| 1 | 2011-02-10 Stefan Monnier <monnier@iro.umontreal.ca> | 23 | 2011-02-10 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 24 | ||
| 3 | * emacs-lisp/cconv.el (cconv-mutated, cconv-captured) | 25 | * emacs-lisp/cconv.el (cconv-mutated, cconv-captured) |
diff --git a/lisp/emacs-lisp/byte-lexbind.el b/lisp/emacs-lisp/byte-lexbind.el index df463c17549..313c4b6ad0f 100644 --- a/lisp/emacs-lisp/byte-lexbind.el +++ b/lisp/emacs-lisp/byte-lexbind.el | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | ;;; byte-lexbind.el --- Lexical binding support for byte-compiler | 1 | ;;; byte-lexbind.el --- Lexical binding support for byte-compiler |
| 2 | ;; | 2 | ;; |
| 3 | ;; Copyright (C) 2001, 2002, 2010 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 2001, 2002, 2010, 2011 Free Software Foundation, Inc. |
| 4 | ;; | 4 | ;; |
| 5 | ;; Author: Miles Bader <miles@gnu.org> | 5 | ;; Author: Miles Bader <miles@gnu.org> |
| 6 | ;; Keywords: lisp, compiler, lexical binding | 6 | ;; Keywords: lisp, compiler, lexical binding |
| @@ -202,24 +202,25 @@ LFORMINFO." | |||
| 202 | (byte-compile-lvarinfo-note-set vinfo) | 202 | (byte-compile-lvarinfo-note-set vinfo) |
| 203 | (byte-compile-lforminfo-note-closure lforminfo vinfo | 203 | (byte-compile-lforminfo-note-closure lforminfo vinfo |
| 204 | closure-flag))))))) | 204 | closure-flag))))))) |
| 205 | ((eq fun 'catch) | 205 | ((and (eq fun 'catch) (not (eq :fun-body (nth 2 form)))) |
| 206 | ;; tag | 206 | ;; tag |
| 207 | (byte-compile-lforminfo-analyze lforminfo (cadr form) | 207 | (byte-compile-lforminfo-analyze lforminfo (cadr form) |
| 208 | ignore closure-flag) | 208 | ignore closure-flag) |
| 209 | ;; `catch' uses a closure for the body | 209 | ;; `catch' uses a closure for the body |
| 210 | (byte-compile-lforminfo-analyze-forms | 210 | (byte-compile-lforminfo-analyze-forms |
| 211 | lforminfo form 2 | 211 | lforminfo form 2 |
| 212 | ignore | 212 | ignore |
| 213 | (or closure-flag | 213 | (or closure-flag |
| 214 | (and (not byte-compile-use-downward-closures) | 214 | (and (not byte-compile-use-downward-closures) |
| 215 | (byte-compile-lforminfo-make-closure-flag))))) | 215 | (byte-compile-lforminfo-make-closure-flag))))) |
| 216 | ((eq fun 'cond) | 216 | ((eq fun 'cond) |
| 217 | (byte-compile-lforminfo-analyze-clauses lforminfo (cdr form) 0 | 217 | (byte-compile-lforminfo-analyze-clauses lforminfo (cdr form) 0 |
| 218 | ignore closure-flag)) | 218 | ignore closure-flag)) |
| 219 | ((eq fun 'condition-case) | 219 | ((eq fun 'condition-case) |
| 220 | ;; `condition-case' separates its body/handlers into | 220 | ;; `condition-case' separates its body/handlers into |
| 221 | ;; separate closures. | 221 | ;; separate closures. |
| 222 | (unless (or closure-flag byte-compile-use-downward-closures) | 222 | (unless (or (eq (nth 1 form) :fun-body) |
| 223 | closure-flag byte-compile-use-downward-closures) | ||
| 223 | ;; condition case is implemented by calling a function | 224 | ;; condition case is implemented by calling a function |
| 224 | (setq closure-flag (byte-compile-lforminfo-make-closure-flag))) | 225 | (setq closure-flag (byte-compile-lforminfo-make-closure-flag))) |
| 225 | ;; value form | 226 | ;; value form |
| @@ -281,7 +282,8 @@ LFORMINFO." | |||
| 281 | ((eq fun 'quote) | 282 | ((eq fun 'quote) |
| 282 | ;; do nothing | 283 | ;; do nothing |
| 283 | ) | 284 | ) |
| 284 | ((eq fun 'save-window-excursion) | 285 | ((and (eq fun 'save-window-excursion) |
| 286 | (not (eq :fun-body (nth 1 form)))) | ||
| 285 | ;; `save-window-excursion' currently uses a funny implementation | 287 | ;; `save-window-excursion' currently uses a funny implementation |
| 286 | ;; that requires its body forms be put into a closure (it should | 288 | ;; that requires its body forms be put into a closure (it should |
| 287 | ;; be fixed to work more like `save-excursion' etc., do). | 289 | ;; be fixed to work more like `save-excursion' etc., do). |
| @@ -579,6 +581,7 @@ proper scope)." | |||
| 579 | (let ((nclosures | 581 | (let ((nclosures |
| 580 | (and lforminfo (byte-compile-lforminfo-num-closures lforminfo)))) | 582 | (and lforminfo (byte-compile-lforminfo-num-closures lforminfo)))) |
| 581 | (if (or (null lforminfo) | 583 | (if (or (null lforminfo) |
| 584 | (zerop nclosures) | ||
| 582 | (= nclosures byte-compile-current-num-closures)) | 585 | (= nclosures byte-compile-current-num-closures)) |
| 583 | ;; No need to push a heap environment. | 586 | ;; No need to push a heap environment. |
| 584 | nil | 587 | nil |
| @@ -692,5 +695,4 @@ binding slots have been popped." | |||
| 692 | 695 | ||
| 693 | (provide 'byte-lexbind) | 696 | (provide 'byte-lexbind) |
| 694 | 697 | ||
| 695 | ;;; arch-tag: b8f1dff6-9edb-4430-a96f-323d42a681a9 | ||
| 696 | ;;; byte-lexbind.el ends here | 698 | ;;; byte-lexbind.el ends here |
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index e14ecc608c7..f37d7489e9a 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el | |||
| @@ -2745,7 +2745,7 @@ If FORM is a lambda or a macro, byte-compile it as a function." | |||
| 2745 | ;; containing the args and any closed-over variables. | 2745 | ;; containing the args and any closed-over variables. |
| 2746 | (and lexical-binding | 2746 | (and lexical-binding |
| 2747 | (byte-compile-make-lambda-lexenv | 2747 | (byte-compile-make-lambda-lexenv |
| 2748 | fun | 2748 | bytecomp-fun |
| 2749 | byte-compile-lexical-environment))) | 2749 | byte-compile-lexical-environment))) |
| 2750 | (is-closure | 2750 | (is-closure |
| 2751 | ;; This is true if we should be making a closure instead of | 2751 | ;; This is true if we should be making a closure instead of |
| @@ -2804,7 +2804,7 @@ If FORM is a lambda or a macro, byte-compile it as a function." | |||
| 2804 | (let ((code (byte-compile-lambda form add-lambda))) | 2804 | (let ((code (byte-compile-lambda form add-lambda))) |
| 2805 | (if (byte-compile-closure-code-p code) | 2805 | (if (byte-compile-closure-code-p code) |
| 2806 | (byte-compile-make-closure code) | 2806 | (byte-compile-make-closure code) |
| 2807 | ;; A simple lambda is just a constant | 2807 | ;; A simple lambda is just a constant. |
| 2808 | (byte-compile-constant code)))) | 2808 | (byte-compile-constant code)))) |
| 2809 | 2809 | ||
| 2810 | (defun byte-compile-constants-vector () | 2810 | (defun byte-compile-constants-vector () |
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el index 60bc906b60c..af42a2864c9 100644 --- a/lisp/emacs-lisp/cconv.el +++ b/lisp/emacs-lisp/cconv.el | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | ;;; cconv.el --- Closure conversion for statically scoped Emacs lisp. -*- lexical-binding: t -*- | 1 | ;;; cconv.el --- Closure conversion for statically scoped Emacs lisp. -*- lexical-binding: nil -*- |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 2011 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 2011 Free Software Foundation, Inc. |
| 4 | 4 | ||
| @@ -82,8 +82,19 @@ is less than this number.") | |||
| 82 | (defvar cconv-captured+mutated nil | 82 | (defvar cconv-captured+mutated nil |
| 83 | "An intersection between cconv-mutated and cconv-captured lists.") | 83 | "An intersection between cconv-mutated and cconv-captured lists.") |
| 84 | (defvar cconv-lambda-candidates nil | 84 | (defvar cconv-lambda-candidates nil |
| 85 | "List of candidates for lambda lifting") | 85 | "List of candidates for lambda lifting. |
| 86 | 86 | Each candidate has the form (VAR INCLOSURE BINDER PARENTFORM).") | |
| 87 | |||
| 88 | (defun cconv-not-lexical-var-p (var) | ||
| 89 | (or (not (symbolp var)) ; form is not a list | ||
| 90 | (special-variable-p var) | ||
| 91 | ;; byte-compile-bound-variables normally holds both the | ||
| 92 | ;; dynamic and lexical vars, but the bytecomp.el should | ||
| 93 | ;; only call us at the top-level so there shouldn't be | ||
| 94 | ;; any lexical vars in it here. | ||
| 95 | (memq var byte-compile-bound-variables) | ||
| 96 | (memq var '(nil t)) | ||
| 97 | (keywordp var))) | ||
| 87 | 98 | ||
| 88 | (defun cconv-freevars (form &optional fvrs) | 99 | (defun cconv-freevars (form &optional fvrs) |
| 89 | "Find all free variables of given form. | 100 | "Find all free variables of given form. |
| @@ -166,24 +177,17 @@ Returns a list of free variables." | |||
| 166 | (append fvrs fvrs-1))) | 177 | (append fvrs fvrs-1))) |
| 167 | 178 | ||
| 168 | (`(,(and sym (or `defun `defconst `defvar)) . ,_) | 179 | (`(,(and sym (or `defun `defconst `defvar)) . ,_) |
| 169 | ;; we call cconv-freevars only for functions(lambdas) | 180 | ;; We call cconv-freevars only for functions(lambdas) |
| 170 | ;; defun, defconst, defvar are not allowed to be inside | 181 | ;; defun, defconst, defvar are not allowed to be inside |
| 171 | ;; a function(lambda) | 182 | ;; a function (lambda). |
| 183 | ;; FIXME: should be a byte-compile-report-error! | ||
| 172 | (error "Invalid form: %s inside a function" sym)) | 184 | (error "Invalid form: %s inside a function" sym)) |
| 173 | 185 | ||
| 174 | (`(,_ . ,body-forms) ; first element is a function or whatever | 186 | (`(,_ . ,body-forms) ; First element is (like) a function. |
| 175 | (dolist (exp body-forms) | 187 | (dolist (exp body-forms) |
| 176 | (setq fvrs (cconv-freevars exp fvrs))) fvrs) | 188 | (setq fvrs (cconv-freevars exp fvrs))) fvrs) |
| 177 | 189 | ||
| 178 | (_ (if (or (not (symbolp form)) ; form is not a list | 190 | (_ (if (cconv-not-lexical-var-p form) |
| 179 | (special-variable-p form) | ||
| 180 | ;; byte-compile-bound-variables normally holds both the | ||
| 181 | ;; dynamic and lexical vars, but the bytecomp.el should | ||
| 182 | ;; only call us at the top-level so there shouldn't be | ||
| 183 | ;; any lexical vars in it here. | ||
| 184 | (memq form byte-compile-bound-variables) | ||
| 185 | (memq form '(nil t)) | ||
| 186 | (keywordp form)) | ||
| 187 | fvrs | 191 | fvrs |
| 188 | (cons form fvrs))))) | 192 | (cons form fvrs))))) |
| 189 | 193 | ||
| @@ -200,12 +204,13 @@ Returns a list of free variables." | |||
| 200 | -- TOPLEVEL(optional) is a boolean variable, true if we are at the root of AST | 204 | -- TOPLEVEL(optional) is a boolean variable, true if we are at the root of AST |
| 201 | 205 | ||
| 202 | Returns a form where all lambdas don't have any free variables." | 206 | Returns a form where all lambdas don't have any free variables." |
| 207 | (message "Entering cconv-closure-convert...") | ||
| 203 | (let ((cconv-mutated '()) | 208 | (let ((cconv-mutated '()) |
| 204 | (cconv-lambda-candidates '()) | 209 | (cconv-lambda-candidates '()) |
| 205 | (cconv-captured '()) | 210 | (cconv-captured '()) |
| 206 | (cconv-captured+mutated '())) | 211 | (cconv-captured+mutated '())) |
| 207 | ;; Analyse form - fill these variables with new information | 212 | ;; Analyse form - fill these variables with new information |
| 208 | (cconv-analyse-form form '() nil) | 213 | (cconv-analyse-form form '() 0) |
| 209 | ;; Calculate an intersection of cconv-mutated and cconv-captured | 214 | ;; Calculate an intersection of cconv-mutated and cconv-captured |
| 210 | (dolist (mvr cconv-mutated) | 215 | (dolist (mvr cconv-mutated) |
| 211 | (when (memq mvr cconv-captured) ; | 216 | (when (memq mvr cconv-captured) ; |
| @@ -271,7 +276,7 @@ Returns a form where all lambdas don't have any free variables." | |||
| 271 | 276 | ||
| 272 | (dolist (elm varsvalues) ;begin of dolist over varsvalues | 277 | (dolist (elm varsvalues) ;begin of dolist over varsvalues |
| 273 | (let (var value elm-new iscandidate ismutated) | 278 | (let (var value elm-new iscandidate ismutated) |
| 274 | (if (listp elm) ; (let (v1) ...) => (let ((v1 nil)) ...) | 279 | (if (consp elm) ; (let (v1) ...) => (let ((v1 nil)) ...) |
| 275 | (progn | 280 | (progn |
| 276 | (setq var (car elm)) | 281 | (setq var (car elm)) |
| 277 | (setq value (cadr elm))) | 282 | (setq value (cadr elm))) |
| @@ -430,9 +435,7 @@ Returns a form where all lambdas don't have any free variables." | |||
| 430 | (letbinds '()) | 435 | (letbinds '()) |
| 431 | (fvrs-new)) ; list of (closed-var var) | 436 | (fvrs-new)) ; list of (closed-var var) |
| 432 | (dolist (elm varsvalues) | 437 | (dolist (elm varsvalues) |
| 433 | (if (listp elm) | 438 | (setq var (if (consp elm) (car elm) elm)) |
| 434 | (setq var (car elm)) | ||
| 435 | (setq var elm)) | ||
| 436 | 439 | ||
| 437 | (let ((lmenvs-1 lmenvs)) ; just to avoid manipulating | 440 | (let ((lmenvs-1 lmenvs)) ; just to avoid manipulating |
| 438 | (dolist (lmenv lmenvs-1) ; the counter inside the loop | 441 | (dolist (lmenv lmenvs-1) ; the counter inside the loop |
| @@ -490,7 +493,7 @@ Returns a form where all lambdas don't have any free variables." | |||
| 490 | (`(quote . ,_) form) ; quote form | 493 | (`(quote . ,_) form) ; quote form |
| 491 | 494 | ||
| 492 | (`(function . ((lambda ,vars . ,body-forms))) ; function form | 495 | (`(function . ((lambda ,vars . ,body-forms))) ; function form |
| 493 | (let (fvrs-new) ; we remove vars from fvrs | 496 | (let (fvrs-new) ; we remove vars from fvrs |
| 494 | (dolist (elm fvrs) ;i use such a tricky way to avoid side effects | 497 | (dolist (elm fvrs) ;i use such a tricky way to avoid side effects |
| 495 | (when (not (memq elm vars)) | 498 | (when (not (memq elm vars)) |
| 496 | (push elm fvrs-new))) | 499 | (push elm fvrs-new))) |
| @@ -577,7 +580,7 @@ Returns a form where all lambdas don't have any free variables." | |||
| 577 | (`(,(and sym (or `defun `defmacro)) | 580 | (`(,(and sym (or `defun `defmacro)) |
| 578 | ,func ,vars . ,body-forms) | 581 | ,func ,vars . ,body-forms) |
| 579 | (if defs-are-legal | 582 | (if defs-are-legal |
| 580 | (let ((body-new '()) ; the whole body | 583 | (let ((body-new '()) ; the whole body |
| 581 | (body-forms-new '()) ; body w\o docstring and interactive | 584 | (body-forms-new '()) ; body w\o docstring and interactive |
| 582 | (letbind '())) | 585 | (letbind '())) |
| 583 | ; find mutable arguments | 586 | ; find mutable arguments |
| @@ -592,12 +595,11 @@ Returns a form where all lambdas don't have any free variables." | |||
| 592 | (when ismutated | 595 | (when ismutated |
| 593 | (push elm letbind) | 596 | (push elm letbind) |
| 594 | (push elm emvrs)))) | 597 | (push elm emvrs)))) |
| 595 | ;transform body-forms | 598 | ;transform body-forms |
| 596 | (when (stringp (car body-forms)) ; treat docstring well | 599 | (when (stringp (car body-forms)) ; treat docstring well |
| 597 | (push (car body-forms) body-new) | 600 | (push (car body-forms) body-new) |
| 598 | (setq body-forms (cdr body-forms))) | 601 | (setq body-forms (cdr body-forms))) |
| 599 | (when (and (listp (car body-forms)) ; treat (interactive) well | 602 | (when (eq (car-safe (car body-forms)) 'interactive) |
| 600 | (eq (caar body-forms) 'interactive)) | ||
| 601 | (push | 603 | (push |
| 602 | (cconv-closure-convert-rec | 604 | (cconv-closure-convert-rec |
| 603 | (car body-forms) | 605 | (car body-forms) |
| @@ -707,201 +709,171 @@ Returns a form where all lambdas don't have any free variables." | |||
| 707 | `(,func . ,body-forms-new))) | 709 | `(,func . ,body-forms-new))) |
| 708 | 710 | ||
| 709 | (_ | 711 | (_ |
| 710 | (if (memq form fvrs) ;form is a free variable | 712 | (let ((free (memq form fvrs))) |
| 711 | (let* ((numero (position form envs)) | 713 | (if free ;form is a free variable |
| 712 | (var '())) | 714 | (let* ((numero (- (length fvrs) (length free))) |
| 713 | (assert numero) | 715 | (var '())) |
| 714 | (if (null (cdr envs)) | 716 | (assert numero) |
| 715 | (setq var 'env) | 717 | (if (null (cdr envs)) |
| 718 | (setq var 'env) | ||
| 716 | ;replace form => | 719 | ;replace form => |
| 717 | ;(aref env #) | 720 | ;(aref env #) |
| 718 | (setq var `(aref env ,numero))) | 721 | (setq var `(aref env ,numero))) |
| 719 | (if (memq form emvrs) ; form => (car (aref env #)) if mutable | 722 | (if (memq form emvrs) ; form => (car (aref env #)) if mutable |
| 720 | `(car ,var) | 723 | `(car ,var) |
| 721 | var)) | 724 | var)) |
| 722 | (if (memq form emvrs) ; if form is a mutable variable | 725 | (if (memq form emvrs) ; if form is a mutable variable |
| 723 | `(car ,form) ; replace form => (car form) | 726 | `(car ,form) ; replace form => (car form) |
| 724 | form))))) | 727 | form)))))) |
| 725 | 728 | ||
| 726 | (defun cconv-analyse-form (form vars inclosure) | 729 | (defun cconv-analyse-function (args body env parentform inclosure) |
| 727 | 730 | (dolist (arg args) | |
| 731 | (cond | ||
| 732 | ((cconv-not-lexical-var-p arg) | ||
| 733 | (byte-compile-report-error | ||
| 734 | (format "Argument %S is not a lexical variable" arg))) | ||
| 735 | ((eq ?& (aref (symbol-name arg) 0)) nil) ;Ignore &rest, &optional, ... | ||
| 736 | (t (push (list arg inclosure parentform) env)))) ;Push vrs to vars. | ||
| 737 | (dolist (form body) ;Analyse body forms. | ||
| 738 | (cconv-analyse-form form env inclosure))) | ||
| 739 | |||
| 740 | (defun cconv-analyse-form (form env inclosure) | ||
| 728 | "Find mutated variables and variables captured by closure. Analyse | 741 | "Find mutated variables and variables captured by closure. Analyse |
| 729 | lambdas if they are suitable for lambda lifting. | 742 | lambdas if they are suitable for lambda lifting. |
| 730 | -- FORM is a piece of Elisp code after macroexpansion. | 743 | -- FORM is a piece of Elisp code after macroexpansion. |
| 731 | -- MLCVRS is a structure that contains captured and mutated variables. | 744 | -- ENV is a list of variables visible in current lexical environment. |
| 732 | (first MLCVRS) is a list of mutated variables, (second MLCVRS) is a | 745 | Each entry has the form (VAR INCLOSURE BINDER PARENTFORM) |
| 733 | list of candidates for lambda lifting and (third MLCVRS) is a list of | 746 | for let-bound vars and (VAR INCLOSURE PARENTFORM) for function arguments. |
| 734 | variables captured by closure. It should be (nil nil nil) initially. | 747 | -- INCLOSURE is the nesting level within lambdas." |
| 735 | -- VARS is a list of local variables visible in current environment | ||
| 736 | (initially empty). | ||
| 737 | -- INCLOSURE is a boolean variable, true if we are in closure. | ||
| 738 | Initially false" | ||
| 739 | (pcase form | 748 | (pcase form |
| 740 | ; let special form | 749 | ; let special form |
| 741 | (`(,(and (or `let* `let) letsym) ,varsvalues . ,body-forms) | 750 | (`(,(and (or `let* `let) letsym) ,binders . ,body-forms) |
| 742 | |||
| 743 | (when (eq letsym 'let) | ||
| 744 | (dolist (elm varsvalues) ; analyse values | ||
| 745 | (when (listp elm) | ||
| 746 | (cconv-analyse-form (cadr elm) vars inclosure)))) | ||
| 747 | 751 | ||
| 748 | (let ((v nil) | 752 | (let ((orig-env env) |
| 749 | (var nil) | 753 | (var nil) |
| 750 | (value nil) | 754 | (value nil)) |
| 751 | (varstruct nil)) | 755 | (dolist (binder binders) |
| 752 | (dolist (elm varsvalues) | 756 | (if (not (consp binder)) |
| 753 | (if (listp elm) | ||
| 754 | (progn | 757 | (progn |
| 755 | (setq var (car elm)) | 758 | (setq var binder) ; treat the form (let (x) ...) well |
| 756 | (setq value (cadr elm))) | 759 | (setq value nil)) |
| 757 | (progn | 760 | (setq var (car binder)) |
| 758 | (setq var elm) ; treat the form (let (x) ...) well | 761 | (setq value (cadr binder)) |
| 759 | (setq value nil))) | 762 | |
| 760 | 763 | (cconv-analyse-form value (if (eq letsym 'let*) env orig-env) | |
| 761 | (when (eq letsym 'let*) ; analyse value | 764 | inclosure)) |
| 762 | (cconv-analyse-form value vars inclosure)) | 765 | |
| 763 | 766 | (unless (cconv-not-lexical-var-p var) | |
| 764 | (let (vars-new) ; remove the old var | 767 | (let ((varstruct (list var inclosure binder form))) |
| 765 | (dolist (vr vars) | 768 | (push varstruct env) ; Push a new one. |
| 766 | (when (not (eq (car vr) var)) | 769 | |
| 767 | (push vr vars-new))) | 770 | (pcase value |
| 768 | (setq vars vars-new)) | 771 | (`(function (lambda . ,_)) |
| 769 | 772 | ;; If var is a function push it to lambda list. | |
| 770 | (setq varstruct (list var inclosure elm form)) | 773 | (push varstruct cconv-lambda-candidates))))))) |
| 771 | (push varstruct vars) ; push a new one | 774 | |
| 772 | 775 | (dolist (form body-forms) ; Analyse body forms. | |
| 773 | (when (and (listp value) | 776 | (cconv-analyse-form form env inclosure))) |
| 774 | (eq (car value) 'function) | 777 | |
| 775 | (eq (caadr value) 'lambda)) | ||
| 776 | ; if var is a function | ||
| 777 | ; push it to lambda list | ||
| 778 | (push varstruct cconv-lambda-candidates)))) | ||
| 779 | |||
| 780 | (dolist (elm body-forms) ; analyse body forms | ||
| 781 | (cconv-analyse-form elm vars inclosure)) | ||
| 782 | nil) | ||
| 783 | ; defun special form | 778 | ; defun special form |
| 784 | (`(,(or `defun `defmacro) ,func ,vrs . ,body-forms) | 779 | (`(,(or `defun `defmacro) ,func ,vrs . ,body-forms) |
| 785 | (let ((v nil)) | 780 | (when env |
| 786 | (dolist (vr vrs) | 781 | (byte-compile-log-warning |
| 787 | (push (list vr form) vars))) ;push vrs to vars | 782 | (format "Function %S will ignore its context %S" |
| 788 | (dolist (elm body-forms) ; analyse body forms | 783 | func (mapcar #'car env)) |
| 789 | (cconv-analyse-form elm vars inclosure)) | 784 | t :warning)) |
| 790 | nil) | 785 | (cconv-analyse-function vrs body-forms nil form 0)) |
| 791 | 786 | ||
| 792 | (`(function . ((lambda ,vrs . ,body-forms))) | 787 | (`(function (lambda ,vrs . ,body-forms)) |
| 793 | (if inclosure ;we are in closure | 788 | (cconv-analyse-function vrs body-forms env form (1+ inclosure))) |
| 794 | (setq inclosure (+ inclosure 1)) | 789 | |
| 795 | (setq inclosure 1)) | 790 | (`(setq . ,forms) |
| 796 | (let (vars-new) ; update vars | 791 | ;; If a local variable (member of env) is modified by setq then |
| 797 | (dolist (vr vars) ; we do that in such a tricky way | 792 | ;; it is a mutated variable. |
| 798 | (when (not (memq (car vr) vrs)) ; to avoid side effects | ||
| 799 | (push vr vars-new))) | ||
| 800 | (dolist (vr vrs) | ||
| 801 | (push (list vr inclosure form) vars-new)) | ||
| 802 | (setq vars vars-new)) | ||
| 803 | |||
| 804 | (dolist (elm body-forms) | ||
| 805 | (cconv-analyse-form elm vars inclosure)) | ||
| 806 | nil) | ||
| 807 | |||
| 808 | (`(setq . ,forms) ; setq | ||
| 809 | ; if a local variable (member of vars) | ||
| 810 | ; is modified by setq | ||
| 811 | ; then it is a mutated variable | ||
| 812 | (while forms | 793 | (while forms |
| 813 | (let ((v (assq (car forms) vars))) ; v = non nil if visible | 794 | (let ((v (assq (car forms) env))) ; v = non nil if visible |
| 814 | (when v | 795 | (when v |
| 815 | (push v cconv-mutated) | 796 | (push v cconv-mutated) |
| 816 | ;; delete from candidate list for lambda lifting | 797 | ;; Delete from candidate list for lambda lifting. |
| 817 | (setq cconv-lambda-candidates (delq v cconv-lambda-candidates)) | 798 | (setq cconv-lambda-candidates (delq v cconv-lambda-candidates)) |
| 818 | (when inclosure | 799 | (unless (eq inclosure (cadr v)) ;Bound in a different closure level. |
| 819 | ;; test if v is declared as argument for lambda | 800 | (push v cconv-captured)))) |
| 820 | (let* ((thirdv (third v)) | 801 | (cconv-analyse-form (cadr forms) env inclosure) |
| 821 | (isarg (if (listp thirdv) | 802 | (setq forms (cddr forms)))) |
| 822 | (eq (car thirdv) 'function) nil))) | 803 | |
| 823 | (if isarg | 804 | (`((lambda . ,_) . ,_) ; first element is lambda expression |
| 824 | (when (> inclosure (cadr v)) ; when we are in closure | ||
| 825 | (push v cconv-captured)) ; push it to captured vars | ||
| 826 | ;; FIXME more detailed comments needed | ||
| 827 | (push v cconv-captured)))))) | ||
| 828 | (cconv-analyse-form (cadr forms) vars inclosure) | ||
| 829 | (setq forms (cddr forms))) | ||
| 830 | nil) | ||
| 831 | |||
| 832 | (`((lambda . ,_) . ,_) ; first element is lambda expression | ||
| 833 | (dolist (exp `((function ,(car form)) . ,(cdr form))) | 805 | (dolist (exp `((function ,(car form)) . ,(cdr form))) |
| 834 | (cconv-analyse-form exp vars inclosure)) | 806 | (cconv-analyse-form exp env inclosure))) |
| 835 | nil) | ||
| 836 | 807 | ||
| 837 | (`(cond . ,cond-forms) ; cond special form | 808 | (`(cond . ,cond-forms) ; cond special form |
| 838 | (dolist (exp1 cond-forms) | 809 | (dolist (forms cond-forms) |
| 839 | (dolist (exp2 exp1) | 810 | (dolist (form forms) |
| 840 | (cconv-analyse-form exp2 vars inclosure))) | 811 | (cconv-analyse-form form env inclosure)))) |
| 841 | nil) | ||
| 842 | 812 | ||
| 843 | (`(quote . ,_) nil) ; quote form | 813 | (`(quote . ,_) nil) ; quote form |
| 844 | |||
| 845 | (`(function . ,_) nil) ; same as quote | 814 | (`(function . ,_) nil) ; same as quote |
| 846 | 815 | ||
| 847 | (`(condition-case ,var ,protected-form . ,conditions-bodies) | 816 | (`(condition-case ,var ,protected-form . ,handlers) |
| 848 | ;condition-case | 817 | ;; FIXME: The bytecode for condition-case forces us to wrap the |
| 849 | (cconv-analyse-form protected-form vars inclosure) | 818 | ;; form and handlers in closures (for handlers, it's probably |
| 850 | (dolist (exp conditions-bodies) | 819 | ;; unavoidable, but not for the protected form). |
| 851 | (cconv-analyse-form (cadr exp) vars inclosure)) | 820 | (setq inclosure (1+ inclosure)) |
| 852 | nil) | 821 | (cconv-analyse-form protected-form env inclosure) |
| 853 | 822 | (push (list var inclosure form) env) | |
| 854 | (`(,(or `defconst `defvar) ,value) | 823 | (dolist (handler handlers) |
| 855 | (cconv-analyse-form value vars inclosure)) | 824 | (dolist (form (cdr handler)) |
| 825 | (cconv-analyse-form form env inclosure)))) | ||
| 826 | |||
| 827 | ;; FIXME: The bytecode for catch forces us to wrap the body. | ||
| 828 | (`(,(or `catch `unwind-protect) ,form . ,body) | ||
| 829 | (cconv-analyse-form form env inclosure) | ||
| 830 | (setq inclosure (1+ inclosure)) | ||
| 831 | (dolist (form body) | ||
| 832 | (cconv-analyse-form form env inclosure))) | ||
| 833 | |||
| 834 | ;; FIXME: The bytecode for save-window-excursion and the lack of | ||
| 835 | ;; bytecode for track-mouse forces us to wrap the body. | ||
| 836 | (`(,(or `save-window-excursion `track-mouse) . ,body) | ||
| 837 | (setq inclosure (1+ inclosure)) | ||
| 838 | (dolist (form body) | ||
| 839 | (cconv-analyse-form form env inclosure))) | ||
| 840 | |||
| 841 | (`(,(or `defconst `defvar) ,var ,value . ,_) | ||
| 842 | (push var byte-compile-bound-variables) | ||
| 843 | (cconv-analyse-form value env inclosure)) | ||
| 856 | 844 | ||
| 857 | (`(,(or `funcall `apply) ,fun . ,args) | 845 | (`(,(or `funcall `apply) ,fun . ,args) |
| 858 | ;; Here we ignore fun because | 846 | ;; Here we ignore fun because funcall and apply are the only two |
| 859 | ;; funcall and apply are the only two | 847 | ;; functions where we can pass a candidate for lambda lifting as |
| 860 | ;; functions where we can pass a candidate | 848 | ;; argument. So, if we see fun elsewhere, we'll delete it from |
| 861 | ;; for lambda lifting as argument. | 849 | ;; lambda candidate list. |
| 862 | ;; So, if we see fun elsewhere, we'll | 850 | (if (symbolp fun) |
| 863 | ;; delete it from lambda candidate list. | 851 | (let ((lv (assq fun cconv-lambda-candidates))) |
| 864 | 852 | (when lv | |
| 865 | ;; If this funcall and the definition of fun | 853 | (unless (eq (cadr lv) inclosure) |
| 866 | ;; are in different closures - we delete fun from | 854 | (push lv cconv-captured) |
| 867 | ;; canidate list, because it is too complicated | 855 | ;; If this funcall and the definition of fun are in |
| 868 | ;; to manage free variables in this case. | 856 | ;; different closures - we delete fun from candidate |
| 869 | (let ((lv (assq fun cconv-lambda-candidates))) | 857 | ;; list, because it is too complicated to manage free |
| 870 | (when lv | 858 | ;; variables in this case. |
| 871 | (when (not (eq (cadr lv) inclosure)) | 859 | (setq cconv-lambda-candidates |
| 872 | (setq cconv-lambda-candidates | 860 | (delq lv cconv-lambda-candidates))))) |
| 873 | (delq lv cconv-lambda-candidates))))) | 861 | (cconv-analyse-form fun env inclosure)) |
| 874 | 862 | (dolist (form args) | |
| 875 | (dolist (elm args) | 863 | (cconv-analyse-form form env inclosure))) |
| 876 | (cconv-analyse-form elm vars inclosure)) | 864 | |
| 877 | nil) | 865 | (`(,_ . ,body-forms) ; First element is a function or whatever. |
| 878 | 866 | (dolist (form body-forms) | |
| 879 | (`(,_ . ,body-forms) ; first element is a function or whatever | 867 | (cconv-analyse-form form env inclosure))) |
| 880 | (dolist (exp body-forms) | 868 | |
| 881 | (cconv-analyse-form exp vars inclosure)) | 869 | ((pred symbolp) |
| 882 | nil) | 870 | (let ((dv (assq form env))) ; dv = declared and visible |
| 883 | 871 | (when dv | |
| 884 | (_ | 872 | (unless (eq inclosure (cadr dv)) ; capturing condition |
| 885 | (when (and (symbolp form) | 873 | (push dv cconv-captured)) |
| 886 | (not (memq form '(nil t))) | 874 | ;; Delete lambda if it is found here, since it escapes. |
| 887 | (not (keywordp form)) | 875 | (setq cconv-lambda-candidates |
| 888 | (not (special-variable-p form))) | 876 | (delq dv cconv-lambda-candidates))))))) |
| 889 | (let ((dv (assq form vars))) ; dv = declared and visible | ||
| 890 | (when dv | ||
| 891 | (when inclosure | ||
| 892 | ;; test if v is declared as argument of lambda | ||
| 893 | (let* ((thirddv (third dv)) | ||
| 894 | (isarg (if (listp thirddv) | ||
| 895 | (eq (car thirddv) 'function) nil))) | ||
| 896 | (if isarg | ||
| 897 | ;; FIXME add detailed comments | ||
| 898 | (when (> inclosure (cadr dv)) ; capturing condition | ||
| 899 | (push dv cconv-captured)) | ||
| 900 | (push dv cconv-captured)))) | ||
| 901 | ; delete lambda | ||
| 902 | (setq cconv-lambda-candidates ; if it is found here | ||
| 903 | (delq dv cconv-lambda-candidates))))) | ||
| 904 | nil))) | ||
| 905 | 877 | ||
| 906 | (provide 'cconv) | 878 | (provide 'cconv) |
| 907 | ;;; cconv.el ends here | 879 | ;;; cconv.el ends here |
diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el index af8047256e2..bccc60a24e0 100644 --- a/lisp/emacs-lisp/macroexp.el +++ b/lisp/emacs-lisp/macroexp.el | |||
| @@ -29,6 +29,8 @@ | |||
| 29 | 29 | ||
| 30 | ;;; Code: | 30 | ;;; Code: |
| 31 | 31 | ||
| 32 | (eval-when-compile (require 'cl)) | ||
| 33 | |||
| 32 | ;; Bound by the top-level `macroexpand-all', and modified to include any | 34 | ;; Bound by the top-level `macroexpand-all', and modified to include any |
| 33 | ;; macros defined by `defmacro'. | 35 | ;; macros defined by `defmacro'. |
| 34 | (defvar macroexpand-all-environment nil) | 36 | (defvar macroexpand-all-environment nil) |
| @@ -164,6 +166,17 @@ Assumes the caller has bound `macroexpand-all-environment'." | |||
| 164 | (cons (macroexpand-all-1 | 166 | (cons (macroexpand-all-1 |
| 165 | (list 'function f)) | 167 | (list 'function f)) |
| 166 | (macroexpand-all-forms args))))) | 168 | (macroexpand-all-forms args))))) |
| 169 | ;; Macro expand compiler macros. | ||
| 170 | ;; FIXME: Don't depend on CL. | ||
| 171 | (`(,(and (pred symbolp) fun | ||
| 172 | (guard (and (eq (get fun 'byte-compile) | ||
| 173 | 'cl-byte-compile-compiler-macro) | ||
| 174 | (functionp 'compiler-macroexpand)))) | ||
| 175 | . ,_) | ||
| 176 | (let ((newform (compiler-macroexpand form))) | ||
| 177 | (if (eq form newform) | ||
| 178 | (macroexpand-all-forms form 1) | ||
| 179 | (macroexpand-all-1 newform)))) | ||
| 167 | (`(,_ . ,_) | 180 | (`(,_ . ,_) |
| 168 | ;; For every other list, we just expand each argument (for | 181 | ;; For every other list, we just expand each argument (for |
| 169 | ;; setq/setq-default this works alright because the variable names | 182 | ;; setq/setq-default this works alright because the variable names |