]> git.eshelyaron.com Git - emacs.git/commitdiff
src/eval.c: Fix bug#62419
authorStefan Monnier <monnier@iro.umontreal.ca>
Sun, 2 Apr 2023 21:45:58 +0000 (17:45 -0400)
committerStefan Monnier <monnier@iro.umontreal.ca>
Sun, 2 Apr 2023 21:45:58 +0000 (17:45 -0400)
Yup, almost 40 years after ELisp first combined them, buffer-local
and let bindings still don't work quite right :-(

The "automatically buffer-local if set" semantics should follow the
principle that it becomes buffer-local iff the var's current binding
refers to the top-level/global/non-let binding.

* src/eval.c (let_shadows_buffer_binding_p): Disregard non-global
let-bindings.

* test/src/eval-tests.el (eval-test--bug62419): New test.

src/eval.c
test/src/eval-tests.el

index eb40c953f96478c001d411eec39568947021feb6..1a4d3ad03075201ed4a322160c71863ff7d8849b 100644 (file)
@@ -3400,7 +3400,7 @@ DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode,
   return object;
 }
 \f
-/* Return true if SYMBOL currently has a let-binding
+/* Return true if SYMBOL's default currently has a let-binding
    which was made in the buffer that is now current.  */
 
 bool
@@ -3415,6 +3415,7 @@ let_shadows_buffer_binding_p (struct Lisp_Symbol *symbol)
        struct Lisp_Symbol *let_bound_symbol = XSYMBOL (specpdl_symbol (p));
        eassert (let_bound_symbol->u.s.redirect != SYMBOL_VARALIAS);
        if (symbol == let_bound_symbol
+           && p->kind != SPECPDL_LET_LOCAL /* bug#62419 */
            && EQ (specpdl_where (p), buf))
          return 1;
       }
index 1e7edca3bacfcd825db045b33e2657bd33ed6c69..e0a27439ba2aa2ee153b6aa628c60cf185713dc0 100644 (file)
@@ -247,4 +247,23 @@ expressions works for identifiers starting with period."
             (should (equal (string-trim (buffer-string))
                            expected-messages))))))))
 
+(defvar-local eval-test--local-var 'global)
+
+(ert-deftest eval-test--bug62419 ()
+  (with-temp-buffer
+    (setq eval-test--local-var 'first-local)
+    (let ((eval-test--local-var t))
+      (kill-local-variable 'eval-test--local-var)
+      (setq eval-test--local-var 'second-local)
+      (should (eq eval-test--local-var 'second-local)))
+    ;; FIXME: It's not completely clear if exiting the above `let'
+    ;; should restore the buffer-local binding to `first-local'
+    ;; (i.e. reset the value of the second buffer-local binding to the
+    ;; first's initial value) or should do nothing (on the principle that
+    ;; the first buffer-local binding doesn't exists any more so there's
+    ;; nothing to restore).  I think both semantics make sense.
+    ;;(should (eq eval-test--local-var 'first-local))
+    )
+  (should (eq eval-test--local-var 'global)))
+
 ;;; eval-tests.el ends here