Subject: bug#79396: 30.1; cl-copy-list can error but is declared as error-free

From: [email-masked]
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