[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#47552: 27.1; cl-defstruct field names matching read-only variables -
From: |
Stefan Monnier |
Subject: |
bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code |
Date: |
Sun, 18 Jun 2023 15:03:25 -0400 |
User-agent: |
Gnus/5.13 (Gnus v5.13) |
> 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
- bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code, Michael Heerdegen, 2023/06/15
- bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code,
Stefan Monnier <=
- bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code, Stefan Monnier, 2023/06/23
- bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code, Michael Heerdegen, 2023/06/23
- bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code, Stefan Monnier, 2023/06/24
- bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code, Michael Heerdegen, 2023/06/24
- bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code, Stefan Monnier, 2023/06/25
- bug#47552: 27.1; cl-defstruct field names matching read-only variables -> bad code, Michael Heerdegen, 2023/06/25