(defvar cl--bind-enquote) ;Non-nil if &cl-quote was in the formal arglist!
(defvar cl--bind-lets) (defvar cl--bind-forms)
+(defun cl--slet (bindings body)
+ "Like `cl--slet*' but for \"parallel let\"."
+ (cond
+ ((seq-some (lambda (binding) (macroexp--dynamic-variable-p (car binding)))
+ bindings)
+ ;; FIXME: We use `identity' to obfuscate the code enough to
+ ;; circumvent the known bug in `macroexp--unfold-lambda' :-(
+ `(funcall (identity (lambda (,@(mapcar #'car bindings))
+ ,@(macroexp-unprogn body)))
+ ,@(mapcar #'cadr bindings)))
+ ((null (cdr bindings))
+ (macroexp-let* bindings body))
+ (t `(let ,bindings ,@(macroexp-unprogn body)))))
+
(defun cl--slet* (bindings body)
"Like `macroexp-let*' but uses static scoping for all the BINDINGS."
- (pcase-exhaustive bindings
- ('() body)
- (`((,var ,exp) . ,bindings)
- (let ((rest (cl--slet* bindings body)))
- (if (macroexp--dynamic-variable-p var)
- ;; FIXME: We use `identity' to obfuscate the code enough to
- ;; circumvent the known bug in `macroexp--unfold-lambda' :-(
- `(funcall (identity (lambda (,var) ,@(macroexp-unprogn rest))) ,exp)
- (macroexp-let* `((,var ,exp)) rest))))))
+ (if (null bindings) body
+ (cl--slet `(,(car bindings)) (cl--slet* (cdr bindings) body))))
(defun cl--transform-lambda (form bind-block)
"Transform a function form FORM of name BIND-BLOCK.
(list '&rest (car (pop cl--bind-lets))))))))
`((,@(nreverse simple-args) ,@rest-args)
,@header
- ;; Make sure that function arguments are unconditionally statically
- ;; scoped (bug#47552).
+ ;; Function arguments are unconditionally statically scoped (bug#47552).
,(cl--slet* cl--bind-lets
(macroexp-progn
`(,@(nreverse cl--bind-forms)
(cl-defun ,name ,args ,@body))))
(defun cl--defsubst-expand (argns body _simple whole _unsafe &rest argvs)
- (if (and whole (not (cl--safe-expr-p (cons 'progn argvs))))
+ (if (and whole (not (cl--safe-expr-p (macroexp-progn argvs))))
whole
- `(let ,(cl-mapcar #'list argns argvs) ,body)))
+ ;; Function arguments are unconditionally statically scoped (bug#47552).
+ (cl--slet (cl-mapcar #'list argns argvs) body)))
;;; Structures.
(macroexpand form)
(should (string-empty-p messages))))))))
+(defvar cl--test-a)
+
(ert-deftest cl-&key-arguments ()
(cl-flet ((fn (&key x) x))
(should-error (fn :x))
(should (eq (fn :x :a) :a)))
;; In ELisp function arguments are always statically scoped (bug#47552).
- (defvar cl--test-a)
(let ((cl--test-a 'dyn)
;; FIXME: How do we silence the "Lexical argument shadows" warning?
(f (cl-function (lambda (&key cl--test-a b)
(list cl--test-a (symbol-value 'cl--test-a) b)))))
(should (equal (funcall f :cl--test-a 'lex :b 2) '(lex dyn 2)))))
+(cl-defstruct cl--test-s
+ cl--test-a b)
+(ert-deftest cl-defstruct-dynbound-label-47552 ()
+ "Check that labels can have the same name as dynbound vars."
+ (let ((cl--test-a 'dyn))
+ (let ((x (make-cl--test-s :cl--test-a 4 :b cl--test-a)))
+ (should (cl--test-s-p x))
+ (should (equal (cl--test-s-cl--test-a x) 4))
+ (should (equal (cl--test-s-b x) 'dyn)))))
;;; cl-macs-tests.el ends here