Don't propagate lexical variables into inlined functions

Functions compiled when inlined (thus from inside the optimiser)
mustn't retain the lexical environment of the caller or there will be
tears.  See discussion at
https://lists.gnu.org/archive/html/emacs-devel/2021-05/msg01227.html .

Bug found by Stefan Monnier.

* lisp/emacs-lisp/byte-opt.el (byte-compile-inline-expand):
Bind byte-optimize--lexvars to nil when re-entering the compiler
recursively.
* test/lisp/emacs-lisp/bytecomp-resources/bc-test-alpha.el:
* test/lisp/emacs-lisp/bytecomp-resources/bc-test-beta.el: New files.
* test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-defsubst): New test.
This commit is contained in:
Mattias Engdegård 2021-05-27 14:03:14 +02:00
parent 501296f994
commit 40d2970f43
4 changed files with 37 additions and 1 deletions

View file

@ -278,7 +278,10 @@
;; first, and then inline its byte-code. This also has the advantage
;; that the final code does not depend on the order of compilation
;; of ELisp files, making the build more reproducible.
(byte-compile name)
;; Since we are called from inside the optimiser, we need to make
;; sure not to propagate lexvar values.
(dlet ((byte-optimize--lexvars nil))
(byte-compile name))
`(,(symbol-function name) ,@(cdr form))))
(_ ;; Give up on inlining.

View file

@ -0,0 +1,9 @@
;;; -*- lexical-binding: t -*-
(require 'bc-test-beta)
(defun bc-test-alpha-f (x)
(let ((y nil))
(list y (bc-test-beta-f x))))
(provide 'bc-test-alpha)

View file

@ -0,0 +1,6 @@
;;; -*- lexical-binding: t -*-
(defsubst bc-test-beta-f (y)
y)
(provide 'bc-test-beta)

View file

@ -1312,6 +1312,24 @@ compiled correctly."
(funcall f 3))
4)))
(declare-function bc-test-alpha-f (ert-resource-file "bc-test-alpha.el"))
(ert-deftest bytecomp-defsubst ()
;; Check that lexical variables don't leak into inlined code. See
;; https://lists.gnu.org/archive/html/emacs-devel/2021-05/msg01227.html
;; First, remove any trace of the functions and package defined:
(fmakunbound 'bc-test-alpha-f)
(fmakunbound 'bc-test-beta-f)
(setq features (delq 'bc-test-beta features))
;; Byte-compile one file that uses a function from another file that isn't
;; compiled.
(let ((file (ert-resource-file "bc-test-alpha.el"))
(load-path (cons (ert-resource-directory) load-path)))
(byte-compile-file file)
(load-file (concat file "c"))
(should (equal (bc-test-alpha-f 'a) '(nil a)))))
;; Local Variables:
;; no-byte-compile: t
;; End: