mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-24 06:20:43 -08:00
Add macroexp--dynamic-variable-p
This predicate can be used for discriminating between lexically and dynamically bound variables during macro-expansion (only). It is restricted to internal use for the time being. * lisp/emacs-lisp/bytecomp.el (byte-compile-initial-macro-environment): Use macroexpand--all-toplevel. * lisp/emacs-lisp/macroexp.el (macroexp-dynamic-variable-p): New. (macroexp--expand-all): Maintain macroexp--dynvars. (macroexpand-all): Rebind macroexp--dynvars. (macroexpand--all-toplevel): New. (internal-macroexpand-for-load): Use macroexpand--all-toplevel. * src/eval.c (eval_sub): Transfer defvar declarations from Vinternal_interpreter_environment into macroexp--dynvars during lazy macro-expansion. * src/lread.c (readevalloop): Rebind macroexp--dynvars around read-and-evaluate operations. (syms_of_lread): Define macroexp--dynvars. * test/lisp/emacs-lisp/macroexp-resources/vk.el: New file. * test/lisp/emacs-lisp/macroexp-tests.el (macroexp-tests--run-emacs) (macroexp-tests--eval-in-subprocess) (macroexp-tests--byte-compile-in-subprocess) (macroexp--tests-dynamic-variable-p): Add tests.
This commit is contained in:
parent
3259f399d4
commit
8706f6fde1
6 changed files with 244 additions and 21 deletions
|
|
@ -510,7 +510,7 @@ Return the compile-time value of FORM."
|
|||
;; whether to compile as byte-compile-form
|
||||
;; or byte-compile-file-form.
|
||||
(let ((expanded
|
||||
(macroexpand-all
|
||||
(macroexpand--all-toplevel
|
||||
form
|
||||
macroexpand-all-environment)))
|
||||
(eval expanded lexical-binding)
|
||||
|
|
|
|||
|
|
@ -289,6 +289,16 @@ is executed without being compiled first."
|
|||
`(let ,(nreverse bindings) . ,body)
|
||||
(macroexp-progn body)))))
|
||||
|
||||
(defun macroexp--dynamic-variable-p (var)
|
||||
"Whether the variable VAR is dynamically scoped.
|
||||
Only valid during macro-expansion."
|
||||
(defvar byte-compile-bound-variables)
|
||||
(or (not lexical-binding)
|
||||
(special-variable-p var)
|
||||
(memq var macroexp--dynvars)
|
||||
(and (boundp 'byte-compile-bound-variables)
|
||||
(memq var byte-compile-bound-variables))))
|
||||
|
||||
(defun macroexp--expand-all (form)
|
||||
"Expand all macros in FORM.
|
||||
This is an internal version of `macroexpand-all'.
|
||||
|
|
@ -316,28 +326,32 @@ Assumes the caller has bound `macroexpand-all-environment'."
|
|||
(cddr form))
|
||||
(cdr form))
|
||||
form))
|
||||
(`(,(or 'defvar 'defconst) . ,_) (macroexp--all-forms form 2))
|
||||
(`(,(or 'defvar 'defconst) ,(and name (pred symbolp)) . ,_)
|
||||
(push name macroexp--dynvars)
|
||||
(macroexp--all-forms form 2))
|
||||
(`(function ,(and f `(lambda . ,_)))
|
||||
(macroexp--cons 'function
|
||||
(macroexp--cons (macroexp--all-forms f 2)
|
||||
nil
|
||||
(cdr form))
|
||||
form))
|
||||
(let ((macroexp--dynvars macroexp--dynvars))
|
||||
(macroexp--cons 'function
|
||||
(macroexp--cons (macroexp--all-forms f 2)
|
||||
nil
|
||||
(cdr form))
|
||||
form)))
|
||||
(`(,(or 'function 'quote) . ,_) form)
|
||||
(`(,(and fun (or 'let 'let*)) . ,(or `(,bindings . ,body)
|
||||
pcase--dontcare))
|
||||
(macroexp--cons
|
||||
fun
|
||||
(macroexp--cons
|
||||
(macroexp--all-clauses bindings 1)
|
||||
(if (null body)
|
||||
(macroexp-unprogn
|
||||
(macroexp-warn-and-return
|
||||
(format "Empty %s body" fun)
|
||||
nil nil 'compile-only))
|
||||
(macroexp--all-forms body))
|
||||
(cdr form))
|
||||
form))
|
||||
(let ((macroexp--dynvars macroexp--dynvars))
|
||||
(macroexp--cons
|
||||
fun
|
||||
(macroexp--cons
|
||||
(macroexp--all-clauses bindings 1)
|
||||
(if (null body)
|
||||
(macroexp-unprogn
|
||||
(macroexp-warn-and-return
|
||||
(format "Empty %s body" fun)
|
||||
nil nil 'compile-only))
|
||||
(macroexp--all-forms body))
|
||||
(cdr form))
|
||||
form)))
|
||||
(`(,(and fun `(lambda . ,_)) . ,args)
|
||||
;; Embedded lambda in function position.
|
||||
;; If the byte-optimizer is loaded, try to unfold this,
|
||||
|
|
@ -421,6 +435,14 @@ Assumes the caller has bound `macroexpand-all-environment'."
|
|||
If no macros are expanded, FORM is returned unchanged.
|
||||
The second optional arg ENVIRONMENT specifies an environment of macro
|
||||
definitions to shadow the loaded ones for use in file byte-compilation."
|
||||
(let ((macroexpand-all-environment environment)
|
||||
(macroexp--dynvars macroexp--dynvars))
|
||||
(macroexp--expand-all form)))
|
||||
|
||||
;; This function is like `macroexpand-all' but for use with top-level
|
||||
;; forms. It does not dynbind `macroexp--dynvars' because we want
|
||||
;; top-level `defvar' declarations to be recorded in that variable.
|
||||
(defun macroexpand--all-toplevel (form &optional environment)
|
||||
(let ((macroexpand-all-environment environment))
|
||||
(macroexp--expand-all form)))
|
||||
|
||||
|
|
@ -706,7 +728,7 @@ test of free variables in the following ways:
|
|||
(let ((macroexp--pending-eager-loads
|
||||
(cons load-file-name macroexp--pending-eager-loads)))
|
||||
(if full-p
|
||||
(macroexpand-all form)
|
||||
(macroexpand--all-toplevel form)
|
||||
(macroexpand form)))
|
||||
(error
|
||||
;; Hopefully this shouldn't happen thanks to the cycle detection,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue