GNU bug report logs - #47552
27.1; cl-defstruct field names matching read-only variables -> bad code

Previous Next

Package: emacs;

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):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: Matt Armstrong <matt <at> rfc20.org>, 47552 <at> debbugs.gnu.org
Subject: Re: bug#47552: 27.1; cl-defstruct field names matching read-only
 variables -> bad code
Date: Sun, 18 Jun 2023 15:03:25 -0400
> 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.