Subject: 31.0.50; side-effect-free properties not loading with (require 'cl-lib)
From: Stefan Kangas <[email-masked]>
To: [email-masked]
Date: Wed, 12 Feb 2025 19:33:05 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

Requiring `cl-lib` is not enough to get the side-effect-free property on
functions like cl-fifth.  To see this, run emacs -Q and evaluate:

    (progn
      (require 'cl-lib)
      (describe-function 'cl-fifth)
      (function-get 'cl-fifth 'side-effect-free))

Note that the form returns nil, and that the "This function does not
change global state" text is not there in the *Help* buffer.

This can be fixed by adding this to the above recipe:

    (require 'cl-macs)

I have attached a patch that fixes this, and also gets the proper
(put 'cl-foo 'side-effect-free t) forms into cl-loaddefs.el.

If this looks okay, I think we could do the same for the
(side-effect-free error-free) case as well.
From a06b8362b019e09deeca7b06d61b5501f92ba1fc Mon Sep 17 00:00:00 2001
From: Stefan Kangas <[email-masked]>
Date: Thu, 13 Feb 2025 04:24:05 +0100
Subject: [PATCH] Inline side-effect-free declarations in cl-lib.el

* lisp/emacs-lisp/cl-macs.el: Move side-effect-free declarations from
here...
* lisp/emacs-lisp/cl-extra.el (cl-gcd, cl-lcm, cl-isqrt, cl-floor)
(cl-ceiling, cl-truncate, cl-round, cl-mod, cl-rem, cl-signum)
(cl-subseq, cl-list-length, cl-get, cl-getf):
* lisp/emacs-lisp/cl-lib.el (cl-plusp, cl-minusp, cl-oddp, cl-evenp)
(cl-fifth, cl-sixth, cl-seventh, cl-eighth, cl-ninth, cl-tenth)
(cl-ldiff, cl-pairlis):
* lisp/emacs-lisp/cl-seq.el (cl-endp): ...to have them inline here.
---
 lisp/emacs-lisp/cl-extra.el | 18 ++++++++++++++++--
 lisp/emacs-lisp/cl-lib.el   | 28 ++++++++++++++++++++--------
 lisp/emacs-lisp/cl-macs.el  | 10 ----------
 lisp/emacs-lisp/cl-seq.el   |  1 +
 4 files changed, 37 insertions(+), 20 deletions(-)

diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el
index 96260c3aff8..992a7918b18 100644
--- a/lisp/emacs-lisp/cl-extra.el
+++ b/lisp/emacs-lisp/cl-extra.el
@@ -317,6 +317,7 @@ cl--set-frame-visible-p
 ;;;###autoload
 (defun cl-gcd (&rest args)
   "Return the greatest common divisor of the arguments."
+  (declare (side-effect-free t))
   (let ((a (or (pop args) 0)))
     (dolist (b args)
       (while (/= b 0)
@@ -326,6 +327,7 @@ cl-gcd
 ;;;###autoload
 (defun cl-lcm (&rest args)
   "Return the least common multiple of the arguments."
+  (declare (side-effect-free t))
   (if (memq 0 args)
       0
     (let ((a (or (pop args) 1)))
@@ -336,6 +338,7 @@ cl-lcm
 ;;;###autoload
 (defun cl-isqrt (x)
   "Return the integer square root of the (integer) argument X."
+  (declare (side-effect-free t))
   (if (and (integerp x) (> x 0))
       (let ((g (ash 2 (/ (logb x) 2)))
 	    g2)
@@ -348,6 +351,7 @@ cl-isqrt
 (defun cl-floor (x &optional y)
   "Return a list of the floor of X and the fractional part of X.
 With two arguments, return floor and remainder of their quotient."
+  (declare (side-effect-free t))
   (let ((q (floor x y)))
     (list q (- x (if y (* y q) q)))))
 
@@ -355,6 +359,7 @@ cl-floor
 (defun cl-ceiling (x &optional y)
   "Return a list of the ceiling of X and the fractional part of X.
 With two arguments, return ceiling and remainder of their quotient."
+  (declare (side-effect-free t))
   (let ((res (cl-floor x y)))
     (if (= (car (cdr res)) 0) res
       (list (1+ (car res)) (- (car (cdr res)) (or y 1))))))
@@ -363,6 +368,7 @@ cl-ceiling
 (defun cl-truncate (x &optional y)
   "Return a list of the integer part of X and the fractional part of X.
 With two arguments, return truncation and remainder of their quotient."
+  (declare (side-effect-free t))
   (if (eq (>= x 0) (or (null y) (>= y 0)))
       (cl-floor x y) (cl-ceiling x y)))
 
@@ -370,6 +376,7 @@ cl-truncate
 (defun cl-round (x &optional y)
   "Return a list of X rounded to the nearest integer and the remainder.
 With two arguments, return rounding and remainder of their quotient."
+  (declare (side-effect-free t))
   (if y
       (if (and (integerp x) (integerp y))
 	  (let* ((hy (/ y 2))
@@ -388,16 +395,19 @@ cl-round
 ;;;###autoload
 (defun cl-mod (x y)
   "The remainder of X divided by Y, with the same sign as Y."
+  (declare (side-effect-free t))
   (nth 1 (cl-floor x y)))
 
 ;;;###autoload
 (defun cl-rem (x y)
   "The remainder of X divided by Y, with the same sign as X."
+  (declare (side-effect-free t))
   (nth 1 (cl-truncate x y)))
 
 ;;;###autoload
 (defun cl-signum (x)
   "Return 1 if X is positive, -1 if negative, 0 if zero."
+  (declare (side-effect-free t))
   (cond ((> x 0) 1) ((< x 0) -1) (t 0)))
 
 ;;;###autoload
@@ -549,6 +559,7 @@ cl-subseq
 If START or END is negative, it counts from the end.
 Signal an error if START or END are outside of the sequence (i.e
 too large if positive or too small if negative)."
+  (declare (side-effect-free t))
   (declare (gv-setter
             (lambda (new)
               (macroexp-let2 nil new new
@@ -581,6 +592,7 @@ cl-nreconc
 ;;;###autoload
 (defun cl-list-length (x)
   "Return the length of list X.  Return nil if list is circular."
+  (declare (side-effect-free t))
   (cl-check-type x list)
   (condition-case nil
       (length x)
@@ -599,7 +611,8 @@ cl-tailp
 (defun cl-get (sym tag &optional def)
   "Return the value of SYMBOL's PROPNAME property, or DEFAULT if none.
 \n(fn SYMBOL PROPNAME &optional DEFAULT)"
-  (declare (compiler-macro cl--compiler-macro-get)
+  (declare (side-effect-free t)
+           (compiler-macro cl--compiler-macro-get)
            (gv-setter (lambda (store) (ignore def) `(put ,sym ,tag ,store))))
   (cl-getf (symbol-plist sym) tag def))
 (autoload 'cl--compiler-macro-get "cl-macs")
@@ -609,7 +622,8 @@ cl-getf
   "Search PROPLIST for property PROPNAME; return its value or DEFAULT.
 PROPLIST is a list of the sort returned by `symbol-plist'.
 \n(fn PROPLIST PROPNAME &optional DEFAULT)"
-  (declare (gv-expander
+  (declare (side-effect-free t)
+           (gv-expander
             (lambda (do)
               (gv-letplace (getter setter) plist
                 (macroexp-let2* nil ((k tag) (d def))
diff --git a/lisp/emacs-lisp/cl-lib.el b/lisp/emacs-lisp/cl-lib.el
index 20d2c8d6a68..e91f94ee276 100644
--- a/lisp/emacs-lisp/cl-lib.el
+++ b/lisp/emacs-lisp/cl-lib.el
@@ -272,20 +272,24 @@ 'cl-floatp-safe
 
 (defsubst cl-plusp (number)
   "Return t if NUMBER is positive."
+  (declare (side-effect-free t))
   (> number 0))
 
 (defsubst cl-minusp (number)
   "Return t if NUMBER is negative."
+  (declare (side-effect-free t))
   (< number 0))
 
 (defun cl-oddp (integer)
   "Return t if INTEGER is odd."
-  (declare (compiler-macro (lambda (_) `(eq (logand ,integer 1) 1))))
+  (declare (side-effect-free t)
+           (compiler-macro (lambda (_) `(eq (logand ,integer 1) 1))))
   (eq (logand integer 1) 1))
 
 (defun cl-evenp (integer)
   "Return t if INTEGER is even."
-  (declare (compiler-macro (lambda (_) `(eq (logand ,integer 1) 0))))
+  (declare (side-effect-free t)
+           (compiler-macro (lambda (_) `(eq (logand ,integer 1) 0))))
   (eq (logand integer 1) 0))
 
 (defconst cl-digit-char-table
@@ -387,32 +391,38 @@ 'cl-fourth
 
 (defsubst cl-fifth (x)
   "Return the fifth element of the list X."
-  (declare (gv-setter (lambda (store) `(setcar (nthcdr 4 ,x) ,store))))
+  (declare (side-effect-free t)
+           (gv-setter (lambda (store) `(setcar (nthcdr 4 ,x) ,store))))
   (nth 4 x))
 
 (defsubst cl-sixth (x)
   "Return the sixth element of the list X."
-  (declare (gv-setter (lambda (store) `(setcar (nthcdr 5 ,x) ,store))))
+  (declare (side-effect-free t)
+           (gv-setter (lambda (store) `(setcar (nthcdr 5 ,x) ,store))))
   (nth 5 x))
 
 (defsubst cl-seventh (x)
   "Return the seventh element of the list X."
-  (declare (gv-setter (lambda (store) `(setcar (nthcdr 6 ,x) ,store))))
+  (declare (side-effect-free t)
+           (gv-setter (lambda (store) `(setcar (nthcdr 6 ,x) ,store))))
   (nth 6 x))
 
 (defsubst cl-eighth (x)
   "Return the eighth element of the list X."
-  (declare (gv-setter (lambda (store) `(setcar (nthcdr 7 ,x) ,store))))
+  (declare (side-effect-free t)
+           (gv-setter (lambda (store) `(setcar (nthcdr 7 ,x) ,store))))
   (nth 7 x))
 
 (defsubst cl-ninth (x)
   "Return the ninth element of the list X."
-  (declare (gv-setter (lambda (store) `(setcar (nthcdr 8 ,x) ,store))))
+  (declare (side-effect-free t)
+           (gv-setter (lambda (store) `(setcar (nthcdr 8 ,x) ,store))))
   (nth 8 x))
 
 (defsubst cl-tenth (x)
   "Return the tenth element of the list X."
-  (declare (gv-setter (lambda (store) `(setcar (nthcdr 9 ,x) ,store))))
+  (declare (side-effect-free t)
+           (gv-setter (lambda (store) `(setcar (nthcdr 9 ,x) ,store))))
   (nth 9 x))
 
 (defalias 'cl-caaar #'caaar)
@@ -467,6 +477,7 @@ cl-list*
 
 (defun cl-ldiff (list sublist)
   "Return a copy of LIST with the tail SUBLIST removed."
+  (declare (side-effect-free t))
   (let ((res nil))
     (while (and (consp list) (not (eq list sublist)))
       (push (pop list) res))
@@ -530,6 +541,7 @@ cl-pairlis
 Return a new alist composed by associating KEYS to corresponding VALUES;
 the process stops as soon as KEYS or VALUES run out.
 If ALIST is non-nil, the new pairs are prepended to it."
+  (declare (side-effect-free t))
   (nconc (cl-mapcar 'cons keys values) alist))
 
 ;;; Miscellaneous.
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 9b76c8c80a0..18ae83ea590 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -3729,16 +3729,6 @@ cl--compiler-macro-get
 (cl-proclaim '(inline cl-acons cl-map cl-notany cl-notevery cl-revappend
                cl-nreconc))
 
-;;; Things that are side-effect-free.
-(mapc (lambda (x) (function-put x 'side-effect-free t))
-      '(cl-first cl-second cl-third cl-fourth
-        cl-fifth cl-sixth cl-seventh
-        cl-eighth cl-ninth cl-tenth
-        cl-rest cl-endp cl-plusp cl-minusp
-        cl-oddp cl-evenp cl-signum cl-ldiff cl-pairlis cl-gcd
-        cl-lcm cl-isqrt cl-floor cl-ceiling cl-truncate cl-round cl-mod cl-rem
-        cl-subseq cl-list-length cl-get cl-getf))
-
 ;;; Things that are side-effect-and-error-free.
 (mapc (lambda (x) (function-put x 'side-effect-free 'error-free))
       '(cl-list* cl-acons cl-equalp
diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el
index 7a79488f1f5..1878153f811 100644
--- a/lisp/emacs-lisp/cl-seq.el
+++ b/lisp/emacs-lisp/cl-seq.el
@@ -115,6 +115,7 @@ cl-key
 (defun cl-endp (x)
   "Return true if X is the empty list; false if it is a cons.
 Signal an error if X is not a list."
+  (declare (side-effect-free t))
   (cl-check-type x list)
   (null x))
 
-- 
2.48.1