[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
scratch/bug-55156 80ba4c1707: eval.c: New functions `defvar-1` and `defc
From: |
Stefan Monnier |
Subject: |
scratch/bug-55156 80ba4c1707: eval.c: New functions `defvar-1` and `defconst-1` (bug#55156) |
Date: |
Thu, 26 May 2022 12:22:12 -0400 (EDT) |
branch: scratch/bug-55156
commit 80ba4c170756049a101b4e6692882ac30ba5e1a5
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>
eval.c: New functions `defvar-1` and `defconst-1` (bug#55156)
The bytecode interpreter can't directly call special forms, so
the byte-compiler usually converts special forms into some sequence of
byte codes (basically, providing a duplicate definition of the special
form). There are still two exceptions to this: `defconst` and `defvar`,
where the compiler instead generates a convoluted chunk of code like:
(funcall '(lambda (x) (defvar <sym> x <doc>)) <value>)
where the quote makes sure we keep the function non-compiled, so as
to end up running the special form at run time.
Get rid of this workaround by introducing `defvar-1` and `defconst-1`
which provide a *functional* interface to the functionality of the
corresponding special form.
* src/eval.c (defvar, Fdefvar_1, Fdefconst_1): New functions, extracted from
`Fdefvar` and `Fdefconst`.
(Fdefvar, Fdefconst): Use them.
(syms_of_eval): `defsubr` the new functions.
* lisp/emacs-lisp/bytecomp.el (byte-compile-tmp-var): Delete const.
(byte-compile-defvar): Simplify using the new functions.
* doc/lispref/variables.texi (Defining Variables): Adjust the doc of
`defvar` to reflect the actual semantics implemented.
---
doc/lispref/variables.texi | 9 +++---
lisp/emacs-lisp/bytecomp.el | 23 ++++++-------
src/eval.c | 78 ++++++++++++++++++++++++++++++---------------
3 files changed, 68 insertions(+), 42 deletions(-)
diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi
index f0e3f337a6..c29547d00d 100644
--- a/doc/lispref/variables.texi
+++ b/doc/lispref/variables.texi
@@ -527,10 +527,11 @@ If @var{symbol} has a buffer-local binding in the current
buffer,
rather than the buffer-local binding. It sets the default value if
the default value is void. @xref{Buffer-Local Variables}.
-If @var{symbol} is already lexically bound (e.g., if the @code{defvar}
-form occurs in a @code{let} form with lexical binding enabled), then
-@code{defvar} sets the dynamic value. The lexical binding remains in
-effect until its binding construct exits. @xref{Variable Scoping}.
+If @var{symbol} is already let bound (e.g., if the @code{defvar}
+form occurs in a @code{let} form), then @code{defvar} sets the toplevel
+default value, like @code{set-default-toplevel-value}.
+The let binding remains in effect until its binding construct exits.
+@xref{Variable Scoping}.
@cindex @code{eval-defun}, and @code{defvar} forms
@cindex @code{eval-last-sexp}, and @code{defvar} forms
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index d7140ad9e6..ee530f95d0 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -4943,8 +4943,6 @@ binding slots have been popped."
(push (nth 1 (nth 1 form)) byte-compile-global-not-obsolete-vars))
(byte-compile-normal-call form))
-(defconst byte-compile-tmp-var (make-symbol "def-tmp-var"))
-
(defun byte-compile-defvar (form)
;; This is not used for file-level defvar/consts.
(when (and (symbolp (nth 1 form))
@@ -4977,18 +4975,17 @@ binding slots have been popped."
string
"third arg to `%s %s' is not a string: %s"
fun var string))
+ ;; Delegate the actual work to the function version of the
+ ;; special form, named with a "-1" suffix.
(byte-compile-form-do-effect
- (if (cddr form) ; `value' provided
- ;; Quote with `quote' to prevent byte-compiling the body,
- ;; which would lead to an inf-loop.
- `(funcall '(lambda (,byte-compile-tmp-var)
- (,fun ,var ,byte-compile-tmp-var ,@(nthcdr 3 form)))
- ,value)
- (if (eq fun 'defconst)
- ;; This will signal an appropriate error at runtime.
- `(eval ',form)
- ;; A simple (defvar foo) just returns foo.
- `',var)))))
+ (cond
+ ((eq fun 'defconst) `(defconst-1 ',var ,@(nthcdr 2 form)))
+ ((not (cddr form)) `',var) ; A simple (defvar foo) just returns foo.
+ (t `(defvar-1 ',var
+ ;; Don't eval `value' if `defvar' wouldn't eval it either.
+ ,(if (macroexp-const-p value) value
+ `(if (boundp ',var) nil ,value))
+ ,@(nthcdr 3 form)))))))
(defun byte-compile-autoload (form)
(and (macroexp-const-p (nth 1 form))
diff --git a/src/eval.c b/src/eval.c
index 08e2dce61e..c3be1dc12c 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -756,6 +756,33 @@ value. */)
return Qnil;
}
+static Lisp_Object
+defvar (Lisp_Object sym, Lisp_Object initvalue, Lisp_Object docstring, bool
eval)
+{
+ Lisp_Object tem;
+
+ CHECK_SYMBOL (sym);
+
+ tem = Fdefault_boundp (sym);
+
+ /* Do it before evaluating the initial value, for self-references. */
+ Finternal__define_uninitialized_variable (sym, docstring);
+
+ if (NILP (tem))
+ Fset_default (sym, eval ? eval_sub (initvalue) : initvalue);
+ else
+ { /* Check if there is really a global binding rather than just a let
+ binding that shadows the global unboundness of the var. */
+ union specbinding *binding = default_toplevel_binding (sym);
+ if (binding && EQ (specpdl_old_value (binding), Qunbound))
+ {
+ set_specpdl_old_value (binding,
+ eval ? eval_sub (initvalue) : initvalue);
+ }
+ }
+ return sym;
+}
+
DEFUN ("defvar", Fdefvar, Sdefvar, 1, UNEVALLED, 0,
doc: /* Define SYMBOL as a variable, and return SYMBOL.
You are not required to define a variable in order to use it, but
@@ -770,12 +797,10 @@ value. If SYMBOL is buffer-local, its default value is
what is set;
buffer-local values are not affected. If INITVALUE is missing,
SYMBOL's value is not set.
-If SYMBOL has a local binding, then this form affects the local
-binding. This is usually not what you want. Thus, if you need to
-load a file defining variables, with this form or with `defconst' or
-`defcustom', you should always load that file _outside_ any bindings
-for these variables. (`defconst' and `defcustom' behave similarly in
-this respect.)
+If SYMBOL is let-bound, then this form does not affect the local let
+binding but the toplevel default binding instead, like
+`set-toplevel-default-binding`.
+(`defcustom' behaves similarly in this respect.)
The optional argument DOCSTRING is a documentation string for the
variable.
@@ -786,7 +811,7 @@ To define a buffer-local variable, use `defvar-local'.
usage: (defvar SYMBOL &optional INITVALUE DOCSTRING) */)
(Lisp_Object args)
{
- Lisp_Object sym, tem, tail;
+ Lisp_Object sym, tail;
sym = XCAR (args);
tail = XCDR (args);
@@ -798,24 +823,8 @@ usage: (defvar SYMBOL &optional INITVALUE DOCSTRING) */)
if (!NILP (XCDR (tail)) && !NILP (XCDR (XCDR (tail))))
error ("Too many arguments");
Lisp_Object exp = XCAR (tail);
-
- tem = Fdefault_boundp (sym);
tail = XCDR (tail);
-
- /* Do it before evaluating the initial value, for self-references. */
- Finternal__define_uninitialized_variable (sym, CAR (tail));
-
- if (NILP (tem))
- Fset_default (sym, eval_sub (exp));
- else
- { /* Check if there is really a global binding rather than just a let
- binding that shadows the global unboundness of the var. */
- union specbinding *binding = default_toplevel_binding (sym);
- if (binding && EQ (specpdl_old_value (binding), Qunbound))
- {
- set_specpdl_old_value (binding, eval_sub (exp));
- }
- }
+ return defvar (sym, exp, CAR (tail), true);
}
else if (!NILP (Vinternal_interpreter_environment)
&& (SYMBOLP (sym) && !XSYMBOL (sym)->u.s.declared_special))
@@ -834,6 +843,14 @@ usage: (defvar SYMBOL &optional INITVALUE DOCSTRING) */)
return sym;
}
+DEFUN ("defvar-1", Fdefvar_1, Sdefvar_1, 2, 3, 0,
+ doc: /* Like `defvar' but as a function.
+More specifically behaves like (defvar SYM 'INITVALUE DOCSTRING). */)
+ (Lisp_Object sym, Lisp_Object initvalue, Lisp_Object docstring)
+{
+ return defvar (sym, initvalue, docstring, false);
+}
+
DEFUN ("defconst", Fdefconst, Sdefconst, 2, UNEVALLED, 0,
doc: /* Define SYMBOL as a constant variable.
This declares that neither programs nor users should ever change the
@@ -863,9 +880,18 @@ usage: (defconst SYMBOL INITVALUE [DOCSTRING]) */)
error ("Too many arguments");
docstring = XCAR (XCDR (XCDR (args)));
}
+ tem = eval_sub (XCAR (XCDR (args)));
+ return Fdefconst_1 (sym, tem, docstring);
+}
+DEFUN ("defconst-1", Fdefconst_1, Sdefconst_1, 2, 3, 0,
+ doc: /* Like `defconst' but as a function.
+More specifically, behaves like (defconst SYM 'INITVALUE DOCSTRING). */)
+ (Lisp_Object sym, Lisp_Object initvalue, Lisp_Object docstring)
+{
+ CHECK_SYMBOL (sym);
+ Lisp_Object tem = initvalue;
Finternal__define_uninitialized_variable (sym, docstring);
- tem = eval_sub (XCAR (XCDR (args)));
if (!NILP (Vpurify_flag))
tem = Fpurecopy (tem);
Fset_default (sym, tem); /* FIXME: set-default-toplevel-value? */
@@ -4333,9 +4359,11 @@ alist of active lexical bindings. */);
defsubr (&Sdefault_toplevel_value);
defsubr (&Sset_default_toplevel_value);
defsubr (&Sdefvar);
+ defsubr (&Sdefvar_1);
defsubr (&Sdefvaralias);
DEFSYM (Qdefvaralias, "defvaralias");
defsubr (&Sdefconst);
+ defsubr (&Sdefconst_1);
defsubr (&Sinternal__define_uninitialized_variable);
defsubr (&Smake_var_non_special);
defsubr (&Slet);