Compute freevars in cconv-analyse.
[bpt/emacs.git] / lisp / emacs-lisp / cconv.el
CommitLineData
3e21b6a7 1;;; cconv.el --- Closure conversion for statically scoped Emacs lisp. -*- lexical-binding: t -*-
94d11cb5 2
d779e73c
SM
3;; Copyright (C) 2011 Free Software Foundation, Inc.
4
5;; Author: Igor Kuzmin <kzuminig@iro.umontreal.ca>
6;; Maintainer: FSF
7;; Keywords: lisp
8;; Package: emacs
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
94d11cb5
IK
24
25;;; Commentary:
26
27;; This takes a piece of Elisp code, and eliminates all free variables from
28;; lambda expressions. The user entry points are cconv-closure-convert and
29;; cconv-closure-convert-toplevel(for toplevel forms).
d779e73c
SM
30;; All macros should be expanded beforehand.
31;;
32;; Here is a brief explanation how this code works.
33;; Firstly, we analyse the tree by calling cconv-analyse-form.
34;; This function finds all mutated variables, all functions that are suitable
94d11cb5
IK
35;; for lambda lifting and all variables captured by closure. It passes the tree
36;; once, returning a list of three lists.
d779e73c
SM
37;;
38;; Then we calculate the intersection of first and third lists returned by
39;; cconv-analyse form to find all mutated variables that are captured by
40;; closure.
94d11cb5 41
d779e73c
SM
42;; Armed with this data, we call cconv-closure-convert-rec, that rewrites the
43;; tree recursivly, lifting lambdas where possible, building closures where it
94d11cb5
IK
44;; is needed and eliminating mutable variables used in closure.
45;;
46;; We do following replacements :
47;; (lambda (v1 ...) ... fv1 fv2 ...) => (lambda (v1 ... fv1 fv2 ) ... fv1 fv2 .)
48;; if the function is suitable for lambda lifting (if all calls are known)
49;;
876c194c
SM
50;; (lambda (v0 ...) ... fv0 .. fv1 ...) =>
51;; (internal-make-closure (v0 ...) (fv1 ...)
52;; ... (internal-get-closed-var 0) ... (internal-get-closed-var 1) ...)
94d11cb5
IK
53;;
54;; If the function has no free variables, we don't do anything.
94d11cb5 55;;
d779e73c 56;; If a variable is mutated (updated by setq), and it is used in a closure
876c194c 57;; we wrap its definition with list: (list val) and we also replace
d779e73c
SM
58;; var => (car var) wherever this variable is used, and also
59;; (setq var value) => (setcar var value) where it is updated.
94d11cb5 60;;
d779e73c
SM
61;; If defun argument is closure mutable, we letbind it and wrap it's
62;; definition with list.
63;; (defun foo (... mutable-arg ...) ...) =>
64;; (defun foo (... m-arg ...) (let ((m-arg (list m-arg))) ...))
94d11cb5
IK
65;;
66;;; Code:
67
b38b1ec0 68;;; TODO:
876c194c 69;; - pay attention to `interactive': its arg is run in an empty env.
cb9336bd
SM
70;; - canonize code in macro-expand so we don't have to handle (let (var) body)
71;; and other oddities.
3e21b6a7
SM
72;; - Change new byte-code representation, so it directly gives the
73;; number of mandatory and optional arguments as well as whether or
74;; not there's a &rest arg.
b38b1ec0
SM
75;; - warn about unused lexical vars.
76;; - clean up cconv-closure-convert-rec, especially the `let' binding part.
3e21b6a7
SM
77;; - new byte codes for unwind-protect, catch, and condition-case so that
78;; closures aren't needed at all.
b38b1ec0 79
94d11cb5
IK
80(eval-when-compile (require 'cl))
81
82(defconst cconv-liftwhen 3
d779e73c 83 "Try to do lambda lifting if the number of arguments + free variables
94d11cb5 84is less than this number.")
a9de04fa
SM
85;; List of all the variables that are both captured by a closure
86;; and mutated. Each entry in the list takes the form
87;; (BINDER . PARENTFORM) where BINDER is the (VAR VAL) that introduces the
88;; variable (or is just (VAR) for variables not introduced by let).
89(defvar cconv-captured+mutated)
d779e73c 90
a9de04fa
SM
91;; List of candidates for lambda lifting.
92;; Each candidate has the form (BINDER . PARENTFORM). A candidate
93;; is a variable that is only passed to `funcall' or `apply'.
94(defvar cconv-lambda-candidates)
d779e73c 95
a9de04fa
SM
96;; Alist associating to each function body the list of its free variables.
97(defvar cconv-freevars-alist)
94d11cb5
IK
98
99;;;###autoload
295fb2ac
SM
100(defun cconv-closure-convert (form)
101 "Main entry point for closure conversion.
94d11cb5
IK
102-- FORM is a piece of Elisp code after macroexpansion.
103-- TOPLEVEL(optional) is a boolean variable, true if we are at the root of AST
104
105Returns a form where all lambdas don't have any free variables."
b38b1ec0 106 ;; (message "Entering cconv-closure-convert...")
a9de04fa 107 (let ((cconv-freevars-alist '())
94d11cb5 108 (cconv-lambda-candidates '())
d779e73c 109 (cconv-captured+mutated '()))
b38b1ec0 110 ;; Analyse form - fill these variables with new information.
a9de04fa
SM
111 (cconv-analyse-form form '())
112 (setq cconv-freevars-alist (nreverse cconv-freevars-alist))
d779e73c
SM
113 (cconv-closure-convert-rec
114 form ; the tree
115 '() ;
116 '() ; fvrs initially empty
117 '() ; envs initially empty
94d11cb5 118 '()
295fb2ac 119 )))
94d11cb5 120
295fb2ac 121(defconst cconv--dummy-var (make-symbol "ignored"))
b38b1ec0
SM
122
123(defun cconv--set-diff (s1 s2)
124 "Return elements of set S1 that are not in set S2."
125 (let ((res '()))
126 (dolist (x s1)
127 (unless (memq x s2) (push x res)))
128 (nreverse res)))
129
130(defun cconv--set-diff-map (s m)
131 "Return elements of set S that are not in Dom(M)."
132 (let ((res '()))
133 (dolist (x s)
134 (unless (assq x m) (push x res)))
135 (nreverse res)))
136
137(defun cconv--map-diff (m1 m2)
138 "Return the submap of map M1 that has Dom(M2) removed."
139 (let ((res '()))
140 (dolist (x m1)
141 (unless (assq (car x) m2) (push x res)))
142 (nreverse res)))
143
144(defun cconv--map-diff-elem (m x)
145 "Return the map M minus any mapping for X."
146 ;; Here we assume that X appears at most once in M.
147 (let* ((b (assq x m))
148 (res (if b (remq b m) m)))
149 (assert (null (assq x res))) ;; Check the assumption was warranted.
150 res))
94d11cb5 151
b38b1ec0
SM
152(defun cconv--map-diff-set (m s)
153 "Return the map M minus any mapping for elements of S."
154 ;; Here we assume that X appears at most once in M.
155 (let ((res '()))
156 (dolist (b m)
157 (unless (memq (car b) s) (push b res)))
158 (nreverse res)))
159
a9de04fa
SM
160(defun cconv-closure-convert-function (fvrs vars emvrs envs lmenvs body-forms
161 parentform)
162 (assert (equal body-forms (caar cconv-freevars-alist)))
163 (let* ((fvrs-new (cconv--set-diff fvrs vars)) ; Remove vars from fvrs.
164 (fv (cdr (pop cconv-freevars-alist)))
165 (body-forms-new '())
166 (letbind '())
167 (envector nil))
168 (when fv
169 ;; Here we form our environment vector.
170
171 (dolist (elm fv)
172 (push
173 (cconv-closure-convert-rec
174 ;; Remove `elm' from `emvrs' for this call because in case
175 ;; `elm' is a variable that's wrapped in a cons-cell, we
176 ;; want to put the cons-cell itself in the closure, rather
177 ;; than just a copy of its current content.
178 elm (remq elm emvrs) fvrs envs lmenvs)
179 envector)) ; Process vars for closure vector.
180 (setq envector (reverse envector))
181 (setq envs fv)
182 (setq fvrs-new fv)) ; Update substitution list.
183
184 (setq emvrs (cconv--set-diff emvrs vars))
185 (setq lmenvs (cconv--map-diff-set lmenvs vars))
186
187 ;; The difference between envs and fvrs is explained
188 ;; in comment in the beginning of the function.
189 (dolist (var vars)
190 (when (member (cons (list var) parentform) cconv-captured+mutated)
191 (push var emvrs)
192 (push `(,var (list ,var)) letbind)))
193 (dolist (elm body-forms) ; convert function body
194 (push (cconv-closure-convert-rec
195 elm emvrs fvrs-new envs lmenvs)
196 body-forms-new))
197
198 (setq body-forms-new
199 (if letbind `((let ,letbind . ,(reverse body-forms-new)))
200 (reverse body-forms-new)))
201
202 (cond
203 ;if no freevars - do nothing
204 ((null envector)
205 `(function (lambda ,vars . ,body-forms-new)))
206 ; 1 free variable - do not build vector
207 (t
208 `(internal-make-closure
209 ,vars ,envector . ,body-forms-new)))))
210
b38b1ec0 211(defun cconv-closure-convert-rec (form emvrs fvrs envs lmenvs)
d779e73c 212 ;; This function actually rewrites the tree.
94d11cb5
IK
213 "Eliminates all free variables of all lambdas in given forms.
214Arguments:
215-- FORM is a piece of Elisp code after macroexpansion.
b38b1ec0 216-- LMENVS is a list of environments used for lambda-lifting. Initially empty.
94d11cb5
IK
217-- EMVRS is a list that contains mutated variables that are visible
218within current environment.
d779e73c
SM
219-- ENVS is an environment(list of free variables) of current closure.
220Initially empty.
221-- FVRS is a list of variables to substitute in each context.
222Initially empty.
94d11cb5
IK
223
224Returns a form where all lambdas don't have any free variables."
d779e73c 225 ;; What's the difference between fvrs and envs?
94d11cb5
IK
226 ;; Suppose that we have the code
227 ;; (lambda (..) fvr (let ((fvr 1)) (+ fvr 1)))
d779e73c
SM
228 ;; only the first occurrence of fvr should be replaced by
229 ;; (aref env ...).
94d11cb5
IK
230 ;; So initially envs and fvrs are the same thing, but when we descend to
231 ;; the 'let, we delete fvr from fvrs. Why we don't delete fvr from envs?
232 ;; Because in envs the order of variables is important. We use this list
d779e73c
SM
233 ;; to find the number of a specific variable in the environment vector,
234 ;; so we never touch it(unless we enter to the other closure).
235 ;;(if (listp form) (print (car form)) form)
236 (pcase form
295fb2ac 237 (`(,(and letsym (or `let* `let)) ,binders . ,body-forms)
94d11cb5
IK
238
239 ; let and let* special forms
d779e73c 240 (let ((body-forms-new '())
295fb2ac 241 (binders-new '())
d779e73c
SM
242 ;; next for variables needed for delayed push
243 ;; because we should process <value(s)>
244 ;; before we change any arguments
245 (lmenvs-new '()) ;needed only in case of let
246 (emvrs-new '()) ;needed only in case of let
247 (emvr-push) ;needed only in case of let*
248 (lmenv-push)) ;needed only in case of let*
249
295fb2ac
SM
250 (dolist (binder binders)
251 (let* ((value nil)
252 (var (if (not (consp binder))
a9de04fa 253 (prog1 binder (setq binder (list binder)))
295fb2ac
SM
254 (setq value (cadr binder))
255 (car binder)))
256 (new-val
257 (cond
258 ;; Check if var is a candidate for lambda lifting.
a9de04fa
SM
259 ((member (cons binder form) cconv-lambda-candidates)
260 (assert (and (eq (car value) 'function)
261 (eq (car (cadr value)) 'lambda)))
262 (assert (equal (cddr (cadr value))
263 (caar cconv-freevars-alist)))
264 (let* ((fv (cdr (pop cconv-freevars-alist)))
295fb2ac
SM
265 (funargs (cadr (cadr value)))
266 (funcvars (append fv funargs))
267 (funcbodies (cddadr value)) ; function bodies
268 (funcbodies-new '()))
94d11cb5 269 ; lambda lifting condition
295fb2ac 270 (if (or (not fv) (< cconv-liftwhen (length funcvars)))
94d11cb5 271 ; do not lift
295fb2ac
SM
272 (cconv-closure-convert-rec
273 value emvrs fvrs envs lmenvs)
94d11cb5 274 ; lift
295fb2ac
SM
275 (progn
276 (dolist (elm2 funcbodies)
277 (push ; convert function bodies
278 (cconv-closure-convert-rec
279 elm2 emvrs nil envs lmenvs)
280 funcbodies-new))
281 (if (eq letsym 'let*)
282 (setq lmenv-push (cons var fv))
283 (push (cons var fv) lmenvs-new))
94d11cb5
IK
284 ; push lifted function
285
295fb2ac
SM
286 `(function .
287 ((lambda ,funcvars .
288 ,(reverse funcbodies-new))))))))
289
290 ;; Check if it needs to be turned into a "ref-cell".
a9de04fa 291 ((member (cons binder form) cconv-captured+mutated)
295fb2ac
SM
292 ;; Declared variable is mutated and captured.
293 (prog1
294 `(list ,(cconv-closure-convert-rec
295 value emvrs
296 fvrs envs lmenvs))
d779e73c
SM
297 (if (eq letsym 'let*)
298 (setq emvr-push var)
295fb2ac
SM
299 (push var emvrs-new))))
300
301 ;; Normal default case.
302 (t
303 (cconv-closure-convert-rec
304 value emvrs fvrs envs lmenvs)))))
d779e73c
SM
305
306 ;; this piece of code below letbinds free
307 ;; variables of a lambda lifted function
308 ;; if they are redefined in this let
309 ;; example:
310 ;; (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1))
311 ;; Here we can not pass y as parameter because it is
312 ;; redefined. We add a (closed-y y) declaration.
313 ;; We do that even if the function is not used inside
314 ;; this let(*). The reason why we ignore this case is
315 ;; that we can't "look forward" to see if the function
316 ;; is called there or not. To treat well this case we
317 ;; need to traverse the tree one more time to collect this
318 ;; data, and I think that it's not worth it.
319
320 (when (eq letsym 'let*)
321 (let ((closedsym '())
322 (new-lmenv '())
323 (old-lmenv '()))
324 (dolist (lmenv lmenvs)
325 (when (memq var (cdr lmenv))
326 (setq closedsym
327 (make-symbol
328 (concat "closed-" (symbol-name var))))
329 (setq new-lmenv (list (car lmenv)))
330 (dolist (frv (cdr lmenv)) (if (eq frv var)
331 (push closedsym new-lmenv)
332 (push frv new-lmenv)))
333 (setq new-lmenv (reverse new-lmenv))
334 (setq old-lmenv lmenv)))
335 (when new-lmenv
336 (setq lmenvs (remq old-lmenv lmenvs))
337 (push new-lmenv lmenvs)
295fb2ac 338 (push `(,closedsym ,var) binders-new))))
b38b1ec0
SM
339 ;; We push the element after redefined free variables are
340 ;; processed. This is important to avoid the bug when free
341 ;; variable and the function have the same name.
295fb2ac 342 (push (list var new-val) binders-new)
d779e73c
SM
343
344 (when (eq letsym 'let*) ; update fvrs
345 (setq fvrs (remq var fvrs))
346 (setq emvrs (remq var emvrs)) ; remove if redefined
347 (when emvr-push
348 (push emvr-push emvrs)
349 (setq emvr-push nil))
b38b1ec0 350 (setq lmenvs (cconv--map-diff-elem lmenvs var))
d779e73c
SM
351 (when lmenv-push
352 (push lmenv-push lmenvs)
353 (setq lmenv-push nil)))
295fb2ac 354 )) ; end of dolist over binders
d779e73c
SM
355 (when (eq letsym 'let)
356
a9de04fa
SM
357 ;; Here we update emvrs, fvrs and lmenvs lists
358 (setq fvrs (cconv--set-diff-map fvrs binders-new))
359 (setq emvrs (cconv--set-diff-map emvrs binders-new))
360 (setq emvrs (append emvrs emvrs-new))
361 (setq lmenvs (cconv--set-diff-map lmenvs binders-new))
362 (setq lmenvs (append lmenvs lmenvs-new))
d779e73c
SM
363
364 ;; Here we do the same letbinding as for let* above
365 ;; to avoid situation when a free variable of a lambda lifted
366 ;; function got redefined.
367
368 (let ((new-lmenv)
369 (var nil)
370 (closedsym nil)
295fb2ac
SM
371 (letbinds '()))
372 (dolist (binder binders)
373 (setq var (if (consp binder) (car binder) binder))
d779e73c
SM
374
375 (let ((lmenvs-1 lmenvs)) ; just to avoid manipulating
376 (dolist (lmenv lmenvs-1) ; the counter inside the loop
377 (when (memq var (cdr lmenv))
378 (setq closedsym (make-symbol
379 (concat "closed-"
380 (symbol-name var))))
381
382 (setq new-lmenv (list (car lmenv)))
b38b1ec0
SM
383 (dolist (frv (cdr lmenv))
384 (push (if (eq frv var) closedsym frv)
385 new-lmenv))
d779e73c
SM
386 (setq new-lmenv (reverse new-lmenv))
387 (setq lmenvs (remq lmenv lmenvs))
388 (push new-lmenv lmenvs)
389 (push `(,closedsym ,var) letbinds)
390 ))))
295fb2ac 391 (setq binders-new (append binders-new letbinds))))
d779e73c
SM
392
393 (dolist (elm body-forms) ; convert body forms
394 (push (cconv-closure-convert-rec
295fb2ac 395 elm emvrs fvrs envs lmenvs)
d779e73c 396 body-forms-new))
295fb2ac 397 `(,letsym ,(reverse binders-new) . ,(reverse body-forms-new))))
94d11cb5
IK
398 ;end of let let* forms
399
d779e73c
SM
400 ; first element is lambda expression
401 (`(,(and `(lambda . ,_) fun) . ,other-body-forms)
402
403 (let ((other-body-forms-new '()))
404 (dolist (elm other-body-forms)
405 (push (cconv-closure-convert-rec
295fb2ac 406 elm emvrs fvrs envs lmenvs)
d779e73c 407 other-body-forms-new))
295fb2ac
SM
408 `(funcall
409 ,(cconv-closure-convert-rec
410 (list 'function fun) emvrs fvrs envs lmenvs)
411 ,@(nreverse other-body-forms-new))))
d779e73c
SM
412
413 (`(cond . ,cond-forms) ; cond special form
414 (let ((cond-forms-new '()))
415 (dolist (elm cond-forms)
416 (push (let ((elm-new '()))
417 (dolist (elm-2 elm)
418 (push
419 (cconv-closure-convert-rec
295fb2ac 420 elm-2 emvrs fvrs envs lmenvs)
d779e73c
SM
421 elm-new))
422 (reverse elm-new))
423 cond-forms-new))
424 (cons 'cond
425 (reverse cond-forms-new))))
426
3e21b6a7 427 (`(quote . ,_) form)
d779e73c 428
3e21b6a7 429 (`(function (lambda ,vars . ,body-forms)) ; function form
a9de04fa
SM
430 (cconv-closure-convert-function
431 fvrs vars emvrs envs lmenvs body-forms form))
d779e73c 432
876c194c
SM
433 (`(internal-make-closure . ,_)
434 (error "Internal byte-compiler error: cconv called twice"))
435
3e21b6a7 436 (`(function . ,_) form) ; Same as quote.
94d11cb5
IK
437
438 ;defconst, defvar
d779e73c
SM
439 (`(,(and sym (or `defconst `defvar)) ,definedsymbol . ,body-forms)
440
295fb2ac
SM
441 (let ((body-forms-new '()))
442 (dolist (elm body-forms)
443 (push (cconv-closure-convert-rec
444 elm emvrs fvrs envs lmenvs)
445 body-forms-new))
446 (setq body-forms-new (reverse body-forms-new))
447 `(,sym ,definedsymbol . ,body-forms-new)))
d779e73c
SM
448
449 ;defun, defmacro
450 (`(,(and sym (or `defun `defmacro))
451 ,func ,vars . ,body-forms)
a9de04fa
SM
452
453 ;; The freevar data was pushed onto cconv-freevars-alist
454 ;; but we don't need it.
455 (assert (equal body-forms (caar cconv-freevars-alist)))
456 (assert (null (cdar cconv-freevars-alist)))
457 (setq cconv-freevars-alist (cdr cconv-freevars-alist))
458
3e21b6a7
SM
459 (let ((body-new '()) ; The whole body.
460 (body-forms-new '()) ; Body w\o docstring and interactive.
295fb2ac 461 (letbind '()))
3e21b6a7
SM
462 ; Find mutable arguments.
463 (dolist (elm vars)
a9de04fa
SM
464 (when (member (cons (list elm) form) cconv-captured+mutated)
465 (push elm letbind)
466 (push elm emvrs)))
3e21b6a7
SM
467 ;Transform body-forms.
468 (when (stringp (car body-forms)) ; Treat docstring well.
295fb2ac
SM
469 (push (car body-forms) body-new)
470 (setq body-forms (cdr body-forms)))
471 (when (eq (car-safe (car body-forms)) 'interactive)
472 (push (cconv-closure-convert-rec
473 (car body-forms)
474 emvrs fvrs envs lmenvs)
475 body-new)
476 (setq body-forms (cdr body-forms)))
477
478 (dolist (elm body-forms)
479 (push (cconv-closure-convert-rec
480 elm emvrs fvrs envs lmenvs)
481 body-forms-new))
482 (setq body-forms-new (reverse body-forms-new))
d779e73c 483
295fb2ac 484 (if letbind
3e21b6a7 485 ; Letbind mutable arguments.
295fb2ac
SM
486 (let ((binders-new '()))
487 (dolist (elm letbind) (push `(,elm (list ,elm))
488 binders-new))
489 (push `(let ,(reverse binders-new) .
490 ,body-forms-new) body-new)
491 (setq body-new (reverse body-new)))
492 (setq body-new (append (reverse body-new) body-forms-new)))
94d11cb5 493
295fb2ac 494 `(,sym ,func ,vars . ,body-new)))
94d11cb5 495
94d11cb5 496 ;condition-case
295fb2ac 497 (`(condition-case ,var ,protected-form . ,handlers)
876c194c 498 (let ((newform (cconv-closure-convert-rec
295fb2ac
SM
499 `(function (lambda () ,protected-form))
500 emvrs fvrs envs lmenvs)))
d779e73c 501 (setq fvrs (remq var fvrs))
295fb2ac 502 `(condition-case :fun-body ,newform
876c194c
SM
503 ,@(mapcar (lambda (handler)
504 (list (car handler)
505 (cconv-closure-convert-rec
506 (let ((arg (or var cconv--dummy-var)))
507 `(function (lambda (,arg) ,@(cdr handler))))
508 emvrs fvrs envs lmenvs)))
509 handlers))))
295fb2ac
SM
510
511 (`(,(and head (or `catch `unwind-protect)) ,form . ,body)
512 `(,head ,(cconv-closure-convert-rec form emvrs fvrs envs lmenvs)
513 :fun-body
514 ,(cconv-closure-convert-rec `(function (lambda () ,@body))
515 emvrs fvrs envs lmenvs)))
516
e0f57e65
SM
517 (`(track-mouse . ,body)
518 `(track-mouse
295fb2ac
SM
519 :fun-body
520 ,(cconv-closure-convert-rec `(function (lambda () ,@body))
521 emvrs fvrs envs lmenvs)))
d779e73c
SM
522
523 (`(setq . ,forms) ; setq special form
524 (let (prognlist sym sym-new value)
525 (while forms
526 (setq sym (car forms))
527 (setq sym-new (cconv-closure-convert-rec
528 sym
295fb2ac 529 (remq sym emvrs) fvrs envs lmenvs))
d779e73c
SM
530 (setq value
531 (cconv-closure-convert-rec
295fb2ac 532 (cadr forms) emvrs fvrs envs lmenvs))
a9de04fa
SM
533 (cond
534 ((memq sym emvrs) (push `(setcar ,sym-new ,value) prognlist))
535 ((symbolp sym-new) (push `(setq ,sym-new ,value) prognlist))
536 ;; This should never happen, but for variables which are
537 ;; mutated+captured+unused, we may end up trying to `setq'
538 ;; on a closed-over variable, so just drop the setq.
539 (t (push value prognlist)))
d779e73c
SM
540 (setq forms (cddr forms)))
541 (if (cdr prognlist)
542 `(progn . ,(reverse prognlist))
543 (car prognlist))))
544
545 (`(,(and (or `funcall `apply) callsym) ,fun . ,args)
546 ; funcall is not a special form
547 ; but we treat it separately
548 ; for the needs of lambda lifting
549 (let ((fv (cdr (assq fun lmenvs))))
550 (if fv
551 (let ((args-new '())
552 (processed-fv '()))
553 ;; All args (free variables and actual arguments)
554 ;; should be processed, because they can be fvrs
555 ;; (free variables of another closure)
556 (dolist (fvr fv)
557 (push (cconv-closure-convert-rec
558 fvr (remq fvr emvrs)
295fb2ac 559 fvrs envs lmenvs)
d779e73c
SM
560 processed-fv))
561 (setq processed-fv (reverse processed-fv))
562 (dolist (elm args)
563 (push (cconv-closure-convert-rec
295fb2ac 564 elm emvrs fvrs envs lmenvs)
d779e73c
SM
565 args-new))
566 (setq args-new (append processed-fv (reverse args-new)))
567 (setq fun (cconv-closure-convert-rec
295fb2ac 568 fun emvrs fvrs envs lmenvs))
d779e73c
SM
569 `(,callsym ,fun . ,args-new))
570 (let ((cdr-new '()))
571 (dolist (elm (cdr form))
572 (push (cconv-closure-convert-rec
295fb2ac 573 elm emvrs fvrs envs lmenvs)
d779e73c
SM
574 cdr-new))
575 `(,callsym . ,(reverse cdr-new))))))
576
577 (`(,func . ,body-forms) ; first element is function or whatever
578 ; function-like forms are:
579 ; or, and, if, progn, prog1, prog2,
580 ; while, until
581 (let ((body-forms-new '()))
582 (dolist (elm body-forms)
583 (push (cconv-closure-convert-rec
295fb2ac 584 elm emvrs fvrs envs lmenvs)
d779e73c
SM
585 body-forms-new))
586 (setq body-forms-new (reverse body-forms-new))
587 `(,func . ,body-forms-new)))
588
589 (_
43e67019
SM
590 (let ((free (memq form fvrs)))
591 (if free ;form is a free variable
592 (let* ((numero (- (length fvrs) (length free)))
cb9336bd
SM
593 ;; Replace form => (aref env #)
594 (var `(internal-get-closed-var ,numero)))
43e67019
SM
595 (if (memq form emvrs) ; form => (car (aref env #)) if mutable
596 `(car ,var)
597 var))
598 (if (memq form emvrs) ; if form is a mutable variable
599 `(car ,form) ; replace form => (car form)
600 form))))))
601
a9de04fa
SM
602(unless (fboundp 'byte-compile-not-lexical-var-p)
603 ;; Only used to test the code in non-lexbind Emacs.
604 (defalias 'byte-compile-not-lexical-var-p 'boundp))
605
606(defun cconv-analyse-use (vardata form)
607 ;; use = `(,binder ,read ,mutated ,captured ,called)
608 (pcase vardata
609 (`(,binder nil ,_ ,_ nil)
610 ;; FIXME: Don't warn about unused fun-args.
611 ;; FIXME: Don't warn about uninterned vars or _ vars.
612 ;; FIXME: This gives warnings in the wrong order and with wrong line
613 ;; number and without function name info.
614 (byte-compile-log-warning (format "Unused variable %S" (car binder))))
615 ;; If it's unused, there's no point converting it into a cons-cell, even if
616 ;; it's captures and mutated.
617 (`(,binder ,_ t t ,_)
618 (push (cons binder form) cconv-captured+mutated))
619 (`(,(and binder `(,_ (function (lambda . ,_)))) nil nil nil t)
620 ;; This is very rare in typical Elisp code. It's probably not really
621 ;; worth the trouble to try and use lambda-lifting in Elisp, but
622 ;; since we coded it up, we might as well use it.
623 (push (cons binder form) cconv-lambda-candidates))
624 (`(,_ ,_ ,_ ,_ ,_) nil)
625 (dontcare)))
626
627(defun cconv-analyse-function (args body env parentform)
628 (let* ((newvars nil)
629 (freevars (list body))
630 ;; We analyze the body within a new environment where all uses are
631 ;; nil, so we can distinguish uses within that function from uses
632 ;; outside of it.
633 (envcopy
634 (mapcar (lambda (vdata) (list (car vdata) nil nil nil nil)) env))
635 (newenv envcopy))
636 ;; Push it before recursing, so cconv-freevars-alist contains entries in
637 ;; the order they'll be used by closure-convert-rec.
638 (push freevars cconv-freevars-alist)
639 (dolist (arg args)
640 (cond
641 ((byte-compile-not-lexical-var-p arg)
642 (byte-compile-report-error
643 (format "Argument %S is not a lexical variable" arg)))
644 ((eq ?& (aref (symbol-name arg) 0)) nil) ;Ignore &rest, &optional, ...
645 (t (let ((varstruct (list arg nil nil nil nil)))
646 (push (cons (list arg) (cdr varstruct)) newvars)
647 (push varstruct newenv)))))
648 (dolist (form body) ;Analyse body forms.
649 (cconv-analyse-form form newenv))
650 ;; Summarize resulting data about arguments.
651 (dolist (vardata newvars)
652 (cconv-analyse-use vardata parentform))
653 ;; Transfer uses collected in `envcopy' (via `newenv') back to `env';
654 ;; and compute free variables.
655 (while env
656 (assert (and envcopy (eq (caar env) (caar envcopy))))
657 (let ((free nil)
658 (x (cdr (car env)))
659 (y (cdr (car envcopy))))
660 (while x
661 (when (car y) (setcar x t) (setq free t))
662 (setq x (cdr x) y (cdr y)))
663 (when free
664 (push (caar env) (cdr freevars))
665 (setf (nth 3 (car env)) t))
666 (setq env (cdr env) envcopy (cdr envcopy))))))
667
668(defun cconv-analyse-form (form env)
669 "Find mutated variables and variables captured by closure.
670Analyse lambdas if they are suitable for lambda lifting.
94d11cb5 671-- FORM is a piece of Elisp code after macroexpansion.
a9de04fa
SM
672-- ENV is an alist mapping each enclosing lexical variable to its info.
673 I.e. each element has the form (VAR . (READ MUTATED CAPTURED CALLED)).
674This function does not return anything but instead fills the
675`cconv-captured+mutated' and `cconv-lambda-candidates' variables
676and updates the data stored in ENV."
94d11cb5
IK
677 (pcase form
678 ; let special form
43e67019 679 (`(,(and (or `let* `let) letsym) ,binders . ,body-forms)
d779e73c 680
43e67019 681 (let ((orig-env env)
a9de04fa 682 (newvars nil)
d779e73c 683 (var nil)
43e67019
SM
684 (value nil))
685 (dolist (binder binders)
686 (if (not (consp binder))
d779e73c 687 (progn
43e67019 688 (setq var binder) ; treat the form (let (x) ...) well
a9de04fa 689 (setq binder (list binder))
43e67019
SM
690 (setq value nil))
691 (setq var (car binder))
692 (setq value (cadr binder))
693
a9de04fa 694 (cconv-analyse-form value (if (eq letsym 'let*) env orig-env)))
43e67019 695
ce5b520a 696 (unless (byte-compile-not-lexical-var-p var)
a9de04fa
SM
697 (let ((varstruct (list var nil nil nil nil)))
698 (push (cons binder (cdr varstruct)) newvars)
699 (push varstruct env))))
43e67019 700
a9de04fa
SM
701 (dolist (form body-forms) ; Analyse body forms.
702 (cconv-analyse-form form env))
43e67019 703
a9de04fa
SM
704 (dolist (vardata newvars)
705 (cconv-analyse-use vardata form))))
43e67019 706
94d11cb5 707 ; defun special form
d779e73c 708 (`(,(or `defun `defmacro) ,func ,vrs . ,body-forms)
43e67019
SM
709 (when env
710 (byte-compile-log-warning
711 (format "Function %S will ignore its context %S"
712 func (mapcar #'car env))
713 t :warning))
a9de04fa 714 (cconv-analyse-function vrs body-forms nil form))
43e67019
SM
715
716 (`(function (lambda ,vrs . ,body-forms))
a9de04fa 717 (cconv-analyse-function vrs body-forms env form))
43e67019
SM
718
719 (`(setq . ,forms)
720 ;; If a local variable (member of env) is modified by setq then
721 ;; it is a mutated variable.
d779e73c 722 (while forms
43e67019 723 (let ((v (assq (car forms) env))) ; v = non nil if visible
a9de04fa
SM
724 (when v (setf (nth 2 v) t)))
725 (cconv-analyse-form (cadr forms) env)
43e67019
SM
726 (setq forms (cddr forms))))
727
728 (`((lambda . ,_) . ,_) ; first element is lambda expression
d779e73c 729 (dolist (exp `((function ,(car form)) . ,(cdr form)))
a9de04fa 730 (cconv-analyse-form exp env)))
d779e73c
SM
731
732 (`(cond . ,cond-forms) ; cond special form
43e67019
SM
733 (dolist (forms cond-forms)
734 (dolist (form forms)
a9de04fa 735 (cconv-analyse-form form env))))
d779e73c
SM
736
737 (`(quote . ,_) nil) ; quote form
d779e73c
SM
738 (`(function . ,_) nil) ; same as quote
739
43e67019
SM
740 (`(condition-case ,var ,protected-form . ,handlers)
741 ;; FIXME: The bytecode for condition-case forces us to wrap the
742 ;; form and handlers in closures (for handlers, it's probably
743 ;; unavoidable, but not for the protected form).
a9de04fa 744 (cconv-analyse-function () (list protected-form) env form)
43e67019 745 (dolist (handler handlers)
a9de04fa 746 (cconv-analyse-function (if var (list var)) (cdr handler) env form)))
43e67019
SM
747
748 ;; FIXME: The bytecode for catch forces us to wrap the body.
749 (`(,(or `catch `unwind-protect) ,form . ,body)
a9de04fa
SM
750 (cconv-analyse-form form env)
751 (cconv-analyse-function () body env form))
43e67019
SM
752
753 ;; FIXME: The bytecode for save-window-excursion and the lack of
754 ;; bytecode for track-mouse forces us to wrap the body.
e0f57e65 755 (`(track-mouse . ,body)
a9de04fa 756 (cconv-analyse-function () body env form))
43e67019
SM
757
758 (`(,(or `defconst `defvar) ,var ,value . ,_)
759 (push var byte-compile-bound-variables)
a9de04fa 760 (cconv-analyse-form value env))
d779e73c
SM
761
762 (`(,(or `funcall `apply) ,fun . ,args)
43e67019
SM
763 ;; Here we ignore fun because funcall and apply are the only two
764 ;; functions where we can pass a candidate for lambda lifting as
765 ;; argument. So, if we see fun elsewhere, we'll delete it from
766 ;; lambda candidate list.
a9de04fa
SM
767 (let ((fdata (and (symbolp fun) (assq fun env))))
768 (if fdata
769 (setf (nth 4 fdata) t)
770 (cconv-analyse-form fun env)))
43e67019 771 (dolist (form args)
a9de04fa 772 (cconv-analyse-form form env)))
43e67019
SM
773
774 (`(,_ . ,body-forms) ; First element is a function or whatever.
775 (dolist (form body-forms)
a9de04fa 776 (cconv-analyse-form form env)))
43e67019
SM
777
778 ((pred symbolp)
779 (let ((dv (assq form env))) ; dv = declared and visible
780 (when dv
a9de04fa 781 (setf (nth 1 dv) t))))))
94d11cb5
IK
782
783(provide 'cconv)
784;;; cconv.el ends here