* lisp/emacs-lisp/cl-macs.el (cl--sm-macroexpand): Handle lambda!

(cl--old-macroexpand): Remove.
(cl--sm-macroexpand): Change its calling convention, so it can use
advice-add.  Extend re-binding treatment of vars so it applies to all
var-introducing forms rather than only to 'let'.
(cl-symbol-macrolet): Use advice-add rather than fset.
This commit is contained in:
Stefan Monnier 2018-02-08 21:40:46 -05:00
parent d34dbc0b69
commit 6b183f85e0
2 changed files with 59 additions and 26 deletions

View file

@ -2089,23 +2089,15 @@ This is like `cl-flet', but for macros instead of functions.
(eval `(cl-function (lambda ,@(cdr res))) t)) (eval `(cl-function (lambda ,@(cdr res))) t))
macroexpand-all-environment)))))) macroexpand-all-environment))))))
(defconst cl--old-macroexpand (defun cl--sm-macroexpand (orig-fun exp &optional env)
(if (and (boundp 'cl--old-macroexpand) "Special macro expander advice used inside `cl-symbol-macrolet'.
(eq (symbol-function 'macroexpand) This function extends `macroexpand' during macro expansion
#'cl--sm-macroexpand)) of `cl-symbol-macrolet' to additionally expand symbol macros."
cl--old-macroexpand
(symbol-function 'macroexpand)))
(defun cl--sm-macroexpand (exp &optional env)
"Special macro expander used inside `cl-symbol-macrolet'.
This function replaces `macroexpand' during macro expansion
of `cl-symbol-macrolet', and does the same thing as `macroexpand'
except that it additionally expands symbol macros."
(let ((macroexpand-all-environment env) (let ((macroexpand-all-environment env)
(venv (alist-get :cl-symbol-macros env))) (venv (alist-get :cl-symbol-macros env)))
(while (while
(progn (progn
(setq exp (funcall cl--old-macroexpand exp env)) (setq exp (funcall orig-fun exp env))
(pcase exp (pcase exp
((pred symbolp) ((pred symbolp)
;; Perform symbol-macro expansion. ;; Perform symbol-macro expansion.
@ -2114,7 +2106,7 @@ except that it additionally expands symbol macros."
(setq exp (cadr symval))))) (setq exp (cadr symval)))))
(`(setq . ,_) (`(setq . ,_)
;; Convert setq to setf if required by symbol-macro expansion. ;; Convert setq to setf if required by symbol-macro expansion.
(let* ((args (mapcar (lambda (f) (cl--sm-macroexpand f env)) (let* ((args (mapcar (lambda (f) (macroexpand f env))
(cdr exp))) (cdr exp)))
(p args)) (p args))
(while (and p (symbolp (car p))) (setq p (cddr p))) (while (and p (symbolp (car p))) (setq p (cddr p)))
@ -2160,10 +2152,10 @@ except that it additionally expands symbol macros."
(list (macroexpand-all (cadr binding) (list (macroexpand-all (cadr binding)
env))))) env)))))
(push (if (assq var venv) (push (if (assq var venv)
;; This binding should hide its symbol-macro, ;; This binding should hide "its" surrounding
;; but given the way macroexpand-all works ;; symbol-macro, but given the way macroexpand-all
;; (i.e. the `env' we receive as input will be ;; works (i.e. the `env' we receive as input will
;; (re)applied to the code we return), we can't ;; be (re)applied to the code we return), we can't
;; prevent application of `env' to the ;; prevent application of `env' to the
;; sub-expressions, so we need to α-rename this ;; sub-expressions, so we need to α-rename this
;; variable instead. ;; variable instead.
@ -2181,6 +2173,43 @@ except that it additionally expands symbol macros."
(macroexpand-all (macroexp-progn body) (macroexpand-all (macroexp-progn body)
env))))) env)))))
nil)) nil))
;; Do the same as for `let' but for variables introduced
;; via other means, such as `lambda' and `condition-case'.
(`(function (lambda ,args . ,body))
(let ((nargs ()) (found nil))
(dolist (var args)
(push (cond
((memq var '(&optional &rest)) var)
((assq var venv)
(let ((nvar (make-symbol (symbol-name var))))
(setq found t)
(push (list var nvar) venv)
(push (cons :cl-symbol-macros venv) env)
nvar))
(t var))
nargs))
(when found
(setq exp `(function
(lambda ,(nreverse nargs)
. ,(mapcar (lambda (exp)
(macroexpand-all exp env))
body)))))
nil))
((and `(condition-case ,var ,exp . ,clauses)
(guard (assq var venv)))
(let ((nvar (make-symbol (symbol-name var))))
(push (list var nvar) venv)
(push (cons :cl-symbol-macros venv) env)
(setq exp
`(condition-case ,nvar ,(macroexpand-all exp env)
. ,(mapcar
(lambda (clause)
`(,(car clause)
. ,(mapcar (lambda (exp)
(macroexpand-all exp env))
(cdr clause))))
clauses)))
nil))
))) )))
exp)) exp))
@ -2192,16 +2221,18 @@ by EXPANSION, and (setq NAME ...) will act like (setf EXPANSION ...).
\(fn ((NAME EXPANSION) ...) FORM...)" \(fn ((NAME EXPANSION) ...) FORM...)"
(declare (indent 1) (debug ((&rest (symbolp sexp)) cl-declarations body))) (declare (indent 1) (debug ((&rest (symbolp sexp)) cl-declarations body)))
(let ((previous-macroexpand (symbol-function 'macroexpand)) (let ((malformed-bindings nil)
(malformed-bindings nil)) (advised (advice-member-p #'cl--sm-macroexpand 'macroexpand)))
(dolist (binding bindings) (dolist (binding bindings)
(unless (and (consp binding) (symbolp (car binding)) (unless (and (consp binding) (symbolp (car binding))
(consp (cdr binding)) (null (cddr binding))) (consp (cdr binding)) (null (cddr binding)))
(push binding malformed-bindings))) (push binding malformed-bindings)))
(unwind-protect (unwind-protect
(progn (progn
(fset 'macroexpand #'cl--sm-macroexpand) (unless advised
(let* ((venv (cdr (assq :cl-symbol-macros macroexpand-all-environment))) (advice-add 'macroexpand :around #'cl--sm-macroexpand))
(let* ((venv (cdr (assq :cl-symbol-macros
macroexpand-all-environment)))
(expansion (expansion
(macroexpand-all (macroexp-progn body) (macroexpand-all (macroexp-progn body)
(cons (cons :cl-symbol-macros (cons (cons :cl-symbol-macros
@ -2213,7 +2244,8 @@ by EXPANSION, and (setq NAME ...) will act like (setf EXPANSION ...).
(nreverse malformed-bindings)) (nreverse malformed-bindings))
expansion) expansion)
expansion))) expansion)))
(fset 'macroexpand previous-macroexpand)))) (unless advised
(advice-remove 'macroexpand #'cl--sm-macroexpand)))))
;;; Multiple values. ;;; Multiple values.

View file

@ -518,13 +518,14 @@
(ert-deftest cl-lib-symbol-macrolet-hide () (ert-deftest cl-lib-symbol-macrolet-hide ()
;; bug#26325 ;; bug#26325, bug#26073
(should (equal (let ((y 5)) (should (equal (let ((y 5))
(cl-symbol-macrolet ((x y)) (cl-symbol-macrolet ((x y))
(list x (list x
(let ((x 6)) (list x y)) (let ((x 6)) (list x y))
(cl-letf ((x 6)) (list x y))))) (cl-letf ((x 6)) (list x y))
'(5 (6 5) (6 6))))) (apply (lambda (x) (+ x 1)) (list 8)))))
'(5 (6 5) (6 6) 9))))
(defun cl-lib-tests--dummy-function () (defun cl-lib-tests--dummy-function ()
;; Dummy function to see if the file is compiled. ;; Dummy function to see if the file is compiled.