]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix local defvar scoping error (bug#46387)
authorMattias Engdegård <mattiase@acm.org>
Wed, 10 Feb 2021 13:26:49 +0000 (14:26 +0100)
committerMattias Engdegård <mattiase@acm.org>
Wed, 10 Feb 2021 13:47:40 +0000 (14:47 +0100)
This bug was introduced by the lexical variable constant propagation
mechanism.  It was discovered by Michael Heerdegen.

* lisp/emacs-lisp/byte-opt.el (byte-optimize-let-form)
(byte-optimize-body): Let the effects of a local defvar declaration be
scoped by let and let*, not any arbitrary Lisp expression body (such
as progn).
* test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-tests--get-vars)
(bytecomp-local-defvar): New test.

lisp/emacs-lisp/byte-opt.el
test/lisp/emacs-lisp/bytecomp-tests.el

index 4fa2c75a889b23a823068aa563a1da984f9b6383..8851f0ef32d22349b13a7c267224c7a9ad409011 100644 (file)
@@ -698,7 +698,8 @@ Same format as `byte-optimize--lexvars', with shared structure and contents.")
               (append new-lexvars byte-optimize--lexvars))
         ;; Walk the body expressions, which may mutate some of the records,
         ;; and generate new bindings that exclude unused variables.
-        (let* ((opt-body (byte-optimize-body (cdr form) for-effect))
+        (let* ((byte-optimize--dynamic-vars byte-optimize--dynamic-vars)
+               (opt-body (byte-optimize-body (cdr form) for-effect))
                (bindings nil))
           (dolist (var let-vars)
             ;; VAR is (NAME EXPR [KEEP [VALUE]])
@@ -730,7 +731,6 @@ Same format as `byte-optimize--lexvars', with shared structure and contents.")
   ;; all-for-effect is true.  returns a new list of forms.
   (let ((rest forms)
        (result nil)
-        (byte-optimize--dynamic-vars byte-optimize--dynamic-vars)
        fe new)
     (while rest
       (setq fe (or all-for-effect (cdr rest)))
index bc623d3efca88bbbcef0293ed9ab1d67d5172b39..0b70c11b2984d80801c77570fb92bcc8021764d6 100644 (file)
@@ -1168,6 +1168,37 @@ mountpoint (Bug#44631)."
       (with-demoted-errors "Error cleaning up directory: %s"
         (delete-directory directory :recursive)))))
 
+(defun bytecomp-tests--get-vars ()
+  (list (ignore-errors (symbol-value 'bytecomp-tests--var1))
+        (ignore-errors (symbol-value 'bytecomp-tests--var2))))
+
+(ert-deftest bytecomp-local-defvar ()
+  "Check that local `defvar' declarations work correctly, both
+interpreted and compiled."
+  (let ((lexical-binding t))
+    (let ((fun '(lambda ()
+                  (defvar bytecomp-tests--var1)
+                  (let ((bytecomp-tests--var1 'a)    ; dynamic
+                        (bytecomp-tests--var2 'b))   ; still lexical
+                    (ignore bytecomp-tests--var2)    ; avoid warning
+                    (bytecomp-tests--get-vars)))))
+      (should (listp fun))      ; Guard against overzealous refactoring!
+      (should (equal (funcall (eval fun t)) '(a nil)))
+      (should (equal (funcall (byte-compile fun)) '(a nil)))
+      )
+
+    ;; `progn' does not constitute a lexical scope for `defvar' (bug#46387).
+    (let ((fun '(lambda ()
+                  (progn
+                    (defvar bytecomp-tests--var1)
+                    (defvar bytecomp-tests--var2))
+                  (let ((bytecomp-tests--var1 'c)
+                        (bytecomp-tests--var2 'd))
+                    (bytecomp-tests--get-vars)))))
+      (should (listp fun))
+      (should (equal (funcall (eval fun t)) '(c d)))
+      (should (equal (funcall (byte-compile fun)) '(c d))))))
+
 ;; Local Variables:
 ;; no-byte-compile: t
 ;; End: