GNU bug report logs -
#47552
27.1; cl-defstruct field names matching read-only variables -> bad code
Previous Next
Reported by: Matt Armstrong <matt <at> rfc20.org>
Date: Thu, 1 Apr 2021 18:39:01 UTC
Severity: normal
Found in versions 27.1, 28.2
Done: Stefan Monnier <monnier <at> iro.umontreal.ca>
Bug is archived. No further changes may be made.
Full log
Message #34 received at 47552 <at> debbugs.gnu.org (full text, mbox):
> But... Indeed doing this "doesn't work":
>
> (cl-defsubst foo4 (&key gcs-done)
> gcs-done)
>
> (foo4 :foo 1)
> -> Debugger entered--Lisp error: (wrong-type-argument numberp nil)
I think the problem is that ELisp function arguments are defined as
being always-statically-scoped, but the macroexpansion of
(cl-defun foo4 (&key gcs-done)
gcs-done)
uses `let` rather than `lambda` to bind `gcs-done`, so it ends up being
dynamically-scoped. Maybe we should introduce something like
(defmacro slet* (bindings &rest body)
(named-let expand ((bindings bindings))
(pcase-exhaustive bindings
('() (macroexp-progn body))
(`((,var ,exp) . ,bindings)
(let ((rest (expand bindings)))
(if (macroexp--dynamic-variable-p var)
`(funcall (lambda (,var) ,rest) ,exp)
(macroexp-let* `((,var ,exp)) rest)))))))
Except I see that `macroexpand-all` will incorrectly turn the
funcall+lambda into a `let`.
Some wise ass knew about it but did it anyway. They even wrote
a comment about it:
;; In lexical-binding mode, let and functions don't bind vars in the same way
;; (let obey special-variable-p, but functions don't). But luckily, this
;; doesn't matter here, because function's behavior is underspecified so it
;; can safely be turned into a `let', even though the reverse is not true.
so we need to either fix that `macroexp--unfold-lambda` or circumvent it
by obfuscating the code, e.g.:
(defmacro slet* (bindings &rest body)
(named-let expand ((bindings bindings))
(pcase-exhaustive bindings
('() (macroexp-progn body))
(`((,var ,exp) . ,bindings)
(let ((rest (expand bindings)))
(if (macroexp--dynamic-variable-p var)
`(funcall (identity (lambda (,var) ,rest)) ,exp)
(macroexp-let* `((,var ,exp)) rest)))))))
Another way to look at it is that maybe we should introduce an
`un-defvar`, such that we can use
(un-defvar gcs-done)
(cl-defun foo4 (&key gcs-done)
gcs-done)
and have `gcs-done` bound statically.
Stefan
This bug report was last modified 2 years and 26 days ago.
Previous Next
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.