GNU bug report logs - #79396
30.1; cl-copy-list can error but is declared as error-free

Previous Next

Package: emacs;

Reported by: mail <at> kisaragi-hiu.com

Date: Sat, 6 Sep 2025 17:42:02 UTC

Severity: normal

Found in version 30.1

Done: Mattias Engdegård <mattias.engdegard <at> gmail.com>

To reply to this bug, email your comments to 79396 AT debbugs.gnu.org.
There is no need to reopen the bug first.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#79396; Package emacs. (Sat, 06 Sep 2025 17:42:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to mail <at> kisaragi-hiu.com:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sat, 06 Sep 2025 17:42:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: mail <at> kisaragi-hiu.com
To: bug-gnu-emacs <at> gnu.org
Subject: 30.1; cl-copy-list can error but is declared as error-free
Date: Sat, 06 Sep 2025 17:40:34 +0000
cl-copy-list can error if given a value that isn't nil or a cons cell,
like (cl-copy-list 3), as expected. However, the function was declared
(side-effect-free error-free), which contradicts this. This means it
gets removed during bytecomp if the value is unused, even when it can
error, resulting in a surprising discrepancy between compiled and not
compiled code.

cl-copy-list probably should be (side-effect-free t), just like
`reverse`.

For a showcase, run these forms in emacs -Q with eval-defun:

  (require 'cl-lib)
  (defun my-compiled-p ()
    "Abuse cl-copy-list to return whether this function is byte-compiled."
    (catch 'ret
      (condition-case _e
          (cl-copy-list 3)
        ;; this line should always happen but currently doesn't.
        (wrong-type-argument (throw 'ret nil)))
      ;; currently after byte comp the call above is removed
      ;; so this line ends up being reached.
      (throw 'ret t)))
  (list (my-compiled-p)
        (progn
          (byte-compile #'my-compiled-p)
          (my-compiled-p)))
  ;; -> (nil t) if both the function wasn't byte-compiled before

This reproduction works in emacs -Q.

Additional information:

- The declaration was added in commit 48ff93ba
  https://cgit.git.savannah.gnu.org/cgit/emacs.git/commit/?h=48ff93ba
  which shipped in Emacs 30.1 and 30.2, and is still present on master.

- Side note: the error is implemented in a weird way by doing (car list)
  after LIST has been checked to not be consp. The car is there just to
  error if LIST is not nil; if LIST is nil, it can only return nil. The
  code has been this way since cl.el was "entered into RCS" in 1993; see
  commit fcd73769.
  https://cgit.git.savannah.gnu.org/cgit/emacs.git/tree/lisp/emacs-lisp/cl.el?h=fcd73769#n561

In GNU Emacs 30.1 (build 1, aarch64-unknown-linux-android) of 2025-04-23
 built on localhost
Configured using:
 'configure --disable-dependency-tracking
 --prefix=/data/data/com.termux/files/usr
 --libdir=/data/data/com.termux/files/usr/lib
 --sbindir=/data/data/com.termux/files/usr/bin --disable-rpath
 --disable-rpath-hack --host=aarch64-linux-android --disable-autodepend
 --with-dumping=none --with-gif=no --with-gnutls --with-jpeg=no
 --with-modules --with-pdumper=yes --with-png=no --with-tiff=no
 --with-xml2 --with-xpm=no --with-tree-sitter --without-dbus
 --without-gconf --without-gsettings --without-lcms2 --without-selinux
 --without-x emacs_cv_alternate_stack=yes emacs_cv_sanitize_address=yes
 emacs_cv_prog_cc_no_pie=no ac_cv_lib_elf_elf_begin=no
 gl_cv_func_dup2_works=no ac_cv_func_setrlimit=no --disable-nls
 --enable-shared --enable-static
 --libexecdir=/data/data/com.termux/files/usr/libexec 'CFLAGS=
 -fstack-protector-strong -Oz' 'CPPFLAGS=
 -isystem/data/data/com.termux/files/usr/include/c++/v1
 -isystem/data/data/com.termux/files/usr/include'
 'LDFLAGS=-L/data/data/com.termux/files/usr/lib
 -Wl,-rpath=/data/data/com.termux/files/usr/lib -Wl,--enable-new-dtags
 -Wl,--as-needed -Wl,-z,relro,-z,now''

Configured features:
GMP GNUTLS LIBXML2 MODULES NOTIFY INOTIFY PDUMPER SECCOMP SQLITE3
THREADS TREE_SITTER XIM ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79396; Package emacs. (Sun, 07 Sep 2025 07:36:02 GMT) Full text and rfc822 format available.

Message #8 received at 79396 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: mail <at> kisaragi-hiu.com, Mattias Engdegård
 <mattias.engdegard <at> gmail.com>
Cc: 79396 <at> debbugs.gnu.org
Subject: Re: bug#79396: 30.1;
 cl-copy-list can error but is declared as error-free
Date: Sun, 07 Sep 2025 10:35:29 +0300
> Date: Sat, 06 Sep 2025 17:40:34 +0000
> TLS-Required: No
> From: mail--- via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
> 
> cl-copy-list can error if given a value that isn't nil or a cons cell,
> like (cl-copy-list 3), as expected. However, the function was declared
> (side-effect-free error-free), which contradicts this. This means it
> gets removed during bytecomp if the value is unused, even when it can
> error, resulting in a surprising discrepancy between compiled and not
> compiled code.
> 
> cl-copy-list probably should be (side-effect-free t), just like
> `reverse`.
> 
> For a showcase, run these forms in emacs -Q with eval-defun:
> 
>   (require 'cl-lib)
>   (defun my-compiled-p ()
>     "Abuse cl-copy-list to return whether this function is byte-compiled."
>     (catch 'ret
>       (condition-case _e
>           (cl-copy-list 3)
>         ;; this line should always happen but currently doesn't.
>         (wrong-type-argument (throw 'ret nil)))
>       ;; currently after byte comp the call above is removed
>       ;; so this line ends up being reached.
>       (throw 'ret t)))
>   (list (my-compiled-p)
>         (progn
>           (byte-compile #'my-compiled-p)
>           (my-compiled-p)))
>   ;; -> (nil t) if both the function wasn't byte-compiled before
> 
> This reproduction works in emacs -Q.
> 
> Additional information:
> 
> - The declaration was added in commit 48ff93ba
>   https://cgit.git.savannah.gnu.org/cgit/emacs.git/commit/?h=48ff93ba
>   which shipped in Emacs 30.1 and 30.2, and is still present on master.
> 
> - Side note: the error is implemented in a weird way by doing (car list)
>   after LIST has been checked to not be consp. The car is there just to
>   error if LIST is not nil; if LIST is nil, it can only return nil. The
>   code has been this way since cl.el was "entered into RCS" in 1993; see
>   commit fcd73769.
>   https://cgit.git.savannah.gnu.org/cgit/emacs.git/tree/lisp/emacs-lisp/cl.el?h=fcd73769#n561

Thanks.

Mattias, any comments?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79396; Package emacs. (Sun, 07 Sep 2025 09:06:02 GMT) Full text and rfc822 format available.

Message #11 received at 79396 <at> debbugs.gnu.org (full text, mbox):

From: Mattias Engdegård <mattias.engdegard <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mail <at> kisaragi-hiu.com, 79396 <at> debbugs.gnu.org,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: Re: bug#79396: 30.1; cl-copy-list can error but is declared as
 error-free
Date: Sun, 7 Sep 2025 11:05:06 +0200
mail--- dixit:

> cl-copy-list can error if given a value that isn't nil or a cons cell,
> like (cl-copy-list 3), as expected. However, the function was declared
> (side-effect-free error-free), which contradicts this.

The error-free declaration is clearly wrong. Sorry about that, and thanks for reporting it.

However, the CL spec doesn't really mandate a specific behaviour when the argument is not a list, so one alternative would be to just have it return its argument in that case. It's attractive because the behaviour would be very clean:

  (cl-copy-list (A . B)) = (cons A (cl-copy-list B))
  (cl-copy-list ATOM) = ATOM

In a way, an atom is the base case of a dotted list.
After such a change, cl-copy-list might qualify as error-free. It will still diverge if given a circular list though.

(CC:ing Stefan in case he has a preference.)





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79396; Package emacs. (Sun, 07 Sep 2025 15:40:01 GMT) Full text and rfc822 format available.

Message #14 received at 79396 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Mattias Engdegård <mattias.engdegard <at> gmail.com>
Cc: mail <at> kisaragi-hiu.com, Eli Zaretskii <eliz <at> gnu.org>, 79396 <at> debbugs.gnu.org
Subject: Re: bug#79396: 30.1; cl-copy-list can error but is declared as
 error-free
Date: Sun, 07 Sep 2025 11:39:35 -0400
> (CC:ing Stefan in case he has a preference.)

I like pure&total functions, so I'm in favor of the change.
I also like strong typing, so I'm not in favor of a change that makes
a "list" function accept silently a non-list argument.
[ I'm so I glad I don't have to be consistent.  ]

Between the two I think pure&total might be a bit more important in this
case (even though the result is still not total), but that's a very
weak preference.  Either way works so it's probably best to let dead
dogs die (i.e. not change the existing behavior).


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79396; Package emacs. (Sun, 07 Sep 2025 15:58:01 GMT) Full text and rfc822 format available.

Message #17 received at 79396 <at> debbugs.gnu.org (full text, mbox):

From: Mattias Engdegård <mattias.engdegard <at> gmail.com>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: mail <at> kisaragi-hiu.com, Eli Zaretskii <eliz <at> gnu.org>, 79396 <at> debbugs.gnu.org
Subject: Re: bug#79396: 30.1; cl-copy-list can error but is declared as
 error-free
Date: Sun, 7 Sep 2025 17:57:25 +0200
7 sep. 2025 kl. 17.39 skrev Stefan Monnier <monnier <at> iro.umontreal.ca>:

> Between the two I think pure&total might be a bit more important in this
> case (even though the result is still not total), but that's a very
> weak preference.  Either way works so it's probably best to let dead
> dogs die (i.e. not change the existing behavior).

Yes, I'm picking the option that requires the least amount of documentation to be written, as is tradition among programmers.
Pushed to master.





bug closed, send any further explanations to 79396 <at> debbugs.gnu.org and mail <at> kisaragi-hiu.com Request was from Mattias Engdegård <mattias.engdegard <at> gmail.com> to control <at> debbugs.gnu.org. (Sun, 07 Sep 2025 16:00:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79396; Package emacs. (Wed, 10 Sep 2025 02:58:01 GMT) Full text and rfc822 format available.

Message #22 received at 79396 <at> debbugs.gnu.org (full text, mbox):

From: Richard Stallman <rms <at> gnu.org>
To: Mattias Engdegård <mattias.engdegard <at> gmail.com>
Cc: mail <at> kisaragi-hiu.com, eliz <at> gnu.org, monnier <at> iro.umontreal.ca,
 79396 <at> debbugs.gnu.org
Subject: Re: bug#79396: 30.1;
 cl-copy-list can error but is declared as error-free
Date: Tue, 09 Sep 2025 22:57:31 -0400
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

If the adequate doc for solution A is less than the adequate doc for
solution B, that is a real advantage for choosing A.  It is not a way
of excusing an inadquate job -- rather, it means that way of doing it
is easier to understand.

-- 
Dr Richard Stallman (https://stallman.org)
Chief GNUisance of the GNU Project (https://gnu.org)
Founder, Free Software Foundation (https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)






This bug report was last modified 3 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.