GNU bug report logs -
#79396
30.1; cl-copy-list can error but is declared as error-free
Previous Next
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.
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):
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):
> 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):
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):
> (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):
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):
[[[ 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.