aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNoam Postavsky2018-06-07 19:58:47 -0400
committerNoam Postavsky2018-06-16 18:34:19 -0400
commit6021e1db92e355fbf5c66765fb0bc4658a80180a (patch)
tree8f25fe69a434b94d34cd1f086dbd9f1f9009d60b
parent05345babc988060cca540770599282102c34f2a7 (diff)
downloademacs-6021e1db92e355fbf5c66765fb0bc4658a80180a.tar.gz
emacs-6021e1db92e355fbf5c66765fb0bc4658a80180a.zip
Don't forget to analyze args of lambda lifted functions (Bug#30872)
* lisp/emacs-lisp/cconv.el (cconv--convert-funcbody): New function. (cconv--convert-function): Extracted from here. (cconv-convert): Also use it here, in the lambda lifted case, so that mutated args are properly accounted for. * test/lisp/emacs-lisp/cconv-tests.el: New test.
-rw-r--r--lisp/emacs-lisp/cconv.el55
-rw-r--r--test/lisp/emacs-lisp/cconv-tests.el40
2 files changed, 71 insertions, 24 deletions
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index ca46dbb7b55..010026b4166 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -206,7 +206,6 @@ Returns a form where all lambdas don't have any free variables."
206 (cl-assert (equal body (caar cconv-freevars-alist))) 206 (cl-assert (equal body (caar cconv-freevars-alist)))
207 (let* ((fvs (cdr (pop cconv-freevars-alist))) 207 (let* ((fvs (cdr (pop cconv-freevars-alist)))
208 (body-new '()) 208 (body-new '())
209 (letbind '())
210 (envector ()) 209 (envector ())
211 (i 0) 210 (i 0)
212 (new-env ())) 211 (new-env ()))
@@ -227,25 +226,8 @@ Returns a form where all lambdas don't have any free variables."
227 (setq envector (nreverse envector)) 226 (setq envector (nreverse envector))
228 (setq new-env (nreverse new-env)) 227 (setq new-env (nreverse new-env))
229 228
230 (dolist (arg args) 229 (setq body-new (cconv--convert-funcbody
231 (if (not (member (cons (list arg) parentform) cconv-captured+mutated)) 230 args body new-env parentform))
232 (if (assq arg new-env) (push `(,arg) new-env))
233 (push `(,arg . (car-safe ,arg)) new-env)
234 (push `(,arg (list ,arg)) letbind)))
235
236 (setq body-new (mapcar (lambda (form)
237 (cconv-convert form new-env nil))
238 body))
239
240 (when letbind
241 (let ((special-forms '()))
242 ;; Keep special forms at the beginning of the body.
243 (while (or (stringp (car body-new)) ;docstring.
244 (memq (car-safe (car body-new)) '(interactive declare)))
245 (push (pop body-new) special-forms))
246 (setq body-new
247 `(,@(nreverse special-forms) (let ,letbind . ,body-new)))))
248
249 (cond 231 (cond
250 ((not (or envector docstring)) ;If no freevars - do nothing. 232 ((not (or envector docstring)) ;If no freevars - do nothing.
251 `(function (lambda ,args . ,body-new))) 233 `(function (lambda ,args . ,body-new)))
@@ -279,6 +261,30 @@ Returns a form where all lambdas don't have any free variables."
279 (nthcdr 3 mapping))))) 261 (nthcdr 3 mapping)))))
280 new-env)) 262 new-env))
281 263
264(defun cconv--convert-funcbody (funargs funcbody env parentform)
265 "Run `cconv-convert' on FUNCBODY, the forms of a lambda expression.
266PARENTFORM is the form containing the lambda expression. ENV is a
267lexical environment (same format as for `cconv-convert'), not
268including FUNARGS, the function's argument list. Return a list
269of converted forms."
270 (let ((letbind ()))
271 (dolist (arg funargs)
272 (if (not (member (cons (list arg) parentform) cconv-captured+mutated))
273 (if (assq arg env) (push `(,arg . nil) env))
274 (push `(,arg . (car-safe ,arg)) env)
275 (push `(,arg (list ,arg)) letbind)))
276 (setq funcbody (mapcar (lambda (form)
277 (cconv-convert form env nil))
278 funcbody))
279 (if letbind
280 (let ((special-forms '()))
281 ;; Keep special forms at the beginning of the body.
282 (while (or (stringp (car funcbody)) ;docstring.
283 (memq (car-safe (car funcbody)) '(interactive declare)))
284 (push (pop funcbody) special-forms))
285 `(,@(nreverse special-forms) (let ,letbind . ,funcbody)))
286 funcbody)))
287
282(defun cconv-convert (form env extend) 288(defun cconv-convert (form env extend)
283 ;; This function actually rewrites the tree. 289 ;; This function actually rewrites the tree.
284 "Return FORM with all its lambdas changed so they are closed. 290 "Return FORM with all its lambdas changed so they are closed.
@@ -292,6 +298,9 @@ ENV is a list where each entry takes the shape either:
292 environment's Nth slot. 298 environment's Nth slot.
293 (VAR . (apply-partially F ARG1 ARG2 ..)): VAR has been λ-lifted and takes 299 (VAR . (apply-partially F ARG1 ARG2 ..)): VAR has been λ-lifted and takes
294 additional arguments ARGs. 300 additional arguments ARGs.
301 (VAR . nil): VAR is accessed normally. This is the same as VAR
302 being absent from ENV, but an explicit nil entry is useful
303 for shadowing VAR for a specific scope.
295EXTEND is a list of variables which might need to be accessed even from places 304EXTEND is a list of variables which might need to be accessed even from places
296where they are shadowed, because some part of ENV causes them to be used at 305where they are shadowed, because some part of ENV causes them to be used at
297places where they originally did not directly appear." 306places where they originally did not directly appear."
@@ -360,10 +369,8 @@ places where they originally did not directly appear."
360 (not (memq fv funargs))) 369 (not (memq fv funargs)))
361 (push `(,fv . (car-safe ,fv)) funcbody-env))) 370 (push `(,fv . (car-safe ,fv)) funcbody-env)))
362 `(function (lambda ,funcvars . 371 `(function (lambda ,funcvars .
363 ,(mapcar (lambda (form) 372 ,(cconv--convert-funcbody
364 (cconv-convert 373 funargs funcbody funcbody-env value)))))
365 form funcbody-env nil))
366 funcbody)))))
367 374
368 ;; Check if it needs to be turned into a "ref-cell". 375 ;; Check if it needs to be turned into a "ref-cell".
369 ((member (cons binder form) cconv-captured+mutated) 376 ((member (cons binder form) cconv-captured+mutated)
diff --git a/test/lisp/emacs-lisp/cconv-tests.el b/test/lisp/emacs-lisp/cconv-tests.el
new file mode 100644
index 00000000000..d14847ce45e
--- /dev/null
+++ b/test/lisp/emacs-lisp/cconv-tests.el
@@ -0,0 +1,40 @@
1;;; cconv-tests.el -*- lexical-binding: t -*-
2
3;; Copyright (C) 2018 Free Software Foundation, Inc.
4
5;; This file is part of GNU Emacs.
6
7;; GNU Emacs is free software: you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
19
20;;; Commentary:
21
22(require 'ert)
23
24(ert-deftest cconv-convert-lambda-lifted ()
25 "Bug#30872."
26 (should
27 (equal (funcall
28 (byte-compile
29 '#'(lambda (handle-fun arg)
30 (let* ((subfun
31 #'(lambda (params)
32 (ignore handle-fun)
33 (funcall #'(lambda () (setq params 42)))
34 params)))
35 (funcall subfun arg))))
36 nil 99)
37 42)))
38
39(provide 'cconv-tests)
40;; cconv-tests.el ends here.