(defvar): Detect defining a variable currently lexically bound

* src/eval.c (lexbound_p): New function.
(Finternal__define_uninitialized_variable): Use it.
This commit is contained in:
Stefan Monnier 2020-11-25 14:49:55 -05:00
parent d148d0259a
commit 1eb168fa97
2 changed files with 53 additions and 0 deletions

View file

@ -1806,6 +1806,11 @@ ledit.el, lmenu.el, lucid.el and old-whitespace.el.
* Lisp Changes in Emacs 28.1
---
** `defvar` detects the error of defining a variable currently lexically bound.
Such mixes are always signs that the outer lexical binding was an
error and should have used dynamic binding instead.
+++
** New completion function 'affixation-function' to add prefix/suffix.
It accepts a list of completions and should return a list where

View file

@ -691,6 +691,45 @@ default_toplevel_binding (Lisp_Object symbol)
return binding;
}
/* Look for a lexical-binding of SYMBOL somewhere up the stack.
This will only find bindings created with interpreted code, since once
compiled names of lexical variables are basically gone anyway. */
static bool
lexbound_p (Lisp_Object symbol)
{
union specbinding *pdl = specpdl_ptr;
while (pdl > specpdl)
{
switch ((--pdl)->kind)
{
case SPECPDL_LET_DEFAULT:
case SPECPDL_LET:
if (EQ (specpdl_symbol (pdl), Qinternal_interpreter_environment))
{
Lisp_Object env = specpdl_old_value (pdl);
if (CONSP (env) && !NILP (Fassq (symbol, env)))
return true;
}
break;
case SPECPDL_UNWIND:
case SPECPDL_UNWIND_ARRAY:
case SPECPDL_UNWIND_PTR:
case SPECPDL_UNWIND_INT:
case SPECPDL_UNWIND_INTMAX:
case SPECPDL_UNWIND_EXCURSION:
case SPECPDL_UNWIND_VOID:
case SPECPDL_BACKTRACE:
case SPECPDL_LET_LOCAL:
break;
default:
emacs_abort ();
}
}
return false;
}
DEFUN ("default-toplevel-value", Fdefault_toplevel_value, Sdefault_toplevel_value, 1, 1, 0,
doc: /* Return SYMBOL's toplevel default value.
"Toplevel" means outside of any let binding. */)
@ -726,6 +765,15 @@ This is like `defvar' and `defconst' but without affecting the variable's
value. */)
(Lisp_Object symbol, Lisp_Object doc)
{
if (!XSYMBOL (symbol)->u.s.declared_special
&& lexbound_p (symbol))
/* This test tries to catch the situation where we do
(let ((<foo-var> ...)) ...(<foo-function> ...)....)
and where the `foo` package only gets loaded when <foo-function>
is called, so the outer `let` incorrectly made the binding lexical
because the <foo-var> wasn't yet declared as dynamic at that point. */
error ("Defining as dynamic an already lexical var");
XSYMBOL (symbol)->u.s.declared_special = true;
if (!NILP (doc))
{