Subject: [PATCH] Make cl-proclaim and cl-declaim obsolete

From: Stefan Kangas <[email-masked]>
Date: Mon, 24 Feb 2025 21:26:06 +0000

Severity: wishlist

In Bug#63288, we discussed making cl-proclaim and cl-declaim obsolete.
There consensus seems to be that this would be good.

The main use of cl-proclaim and cl-declaim is to enable some CL-Lib
specific optimizations by setting cl--optimize-speed and
cl--optimize-safety, and to set some byte-compiler variables.

It does seem quite strange to have such fundamental parts implemented in
cl-lib.el; if they are generally useful they should be made part of
Emacs Lisp proper and integrated as such.  If they are not, we should
just remove them.  In all cases, I think we should avoid maintaining
hard-to-understand and complex hacks in CL-Lib.

However, simply removing these CL-Lib specific variables makes the
compiled file size of eieieo-core.el (our only user of cl-declaim)
increase by almost 20%:

-rw-r--r--  1 skangas  staff  40705 Feb 24 21:31 eieio-core.elc
-rw-r--r--  1 skangas  staff  45572 Feb 24 21:31 lisp/emacs-lisp/eieio-core.elc

I'm a bit reluctant to make eieio slower, even if only a little, since
it's already known for not being fast.  So until we have a replacement
for cl--optimize-speed and cl--optimize-safety, I guess we'll have to
live with them.

Meanwhile, marking cl-proclaim and cl-declaim obsolete should at least
discourage using cl-lib for performance hacks, and hopefully encourage
more work on improving the byte-compiler instead.

The attached is the best patch I could come up with for now.

WDYT?
From 8516e712f9bdf79f07a5c62e41574f7fa47cbfc8 Mon Sep 17 00:00:00 2001
From: Stefan Kangas <[email-masked]>
Date: Mon, 24 Feb 2025 21:54:51 +0100
Subject: [PATCH] Make cl-proclaim and cl-declaim obsolete

* lisp/emacs-lisp/cl-lib.el (cl-proclaim, cl-declaim): Declare obsolete.
(cl--optimize-speed, cl--optimize-safety): Document as deprecated.
* doc/misc/cl.texi (Declarations): Don't document above deleted macros.
* lisp/emacs-lisp/eieio-core.el (eieio--class): Don't use cl-declaim.
---
 doc/misc/cl.texi              | 128 +---------------------------------
 etc/NEWS                      |   6 ++
 lisp/emacs-lisp/cl-lib.el     |   6 +-
 lisp/emacs-lisp/eieio-core.el |  15 ++--
 4 files changed, 21 insertions(+), 134 deletions(-)

diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi
index 862b8f4a1c4..aebb4d7e9f2 100644
--- a/doc/misc/cl.texi
+++ b/doc/misc/cl.texi
@@ -60,7 +60,7 @@ Top
 * Predicates::           Type predicates and equality predicates.
 * Control Structure::    Assignment, conditionals, blocks, looping.
 * Macros::               Destructuring, compiler macros.
-* Declarations::         @code{cl-proclaim}, @code{cl-declaim}, etc.
+* Declarations::         @code{cl-the}.
 * Symbols::              Property lists, creating symbols.
 * Numbers::              Predicates, functions, random numbers.
 * Sequences::            Mapping, functions, searching, sorting.
@@ -2651,36 +2651,12 @@ Declarations
 mechanism that allows you to give the compiler special hints
 about the types of data that will be stored in particular variables,
 and about the ways those variables and functions will be used.  This
-package defines versions of all the Common Lisp declaration forms:
-@code{proclaim}, @code{declaim}, and @code{the}.
+package defines a versions of only one Common Lisp declaration form:
+@code{the}.
 
 Most of the Common Lisp declarations are not currently useful in Emacs
 Lisp.  For example, the byte-code system provides little
 opportunity to benefit from type information.
-@ignore
-and @code{special} declarations are redundant in a fully
-dynamically-scoped Lisp.
-@end ignore
-A few declarations are meaningful when byte compiler optimizations
-are enabled, as they are by default.  Otherwise these
-declarations will effectively be ignored.
-
-@defun cl-proclaim decl-spec
-This function records a ``global'' declaration specified by
-@var{decl-spec}.  Since @code{cl-proclaim} is a function, @var{decl-spec}
-is evaluated and thus should normally be quoted.
-@end defun
-
-@defmac cl-declaim decl-specs@dots{}
-This macro is like @code{cl-proclaim}, except that it takes any number
-of @var{decl-spec} arguments, and the arguments are unevaluated and
-unquoted.  The @code{cl-declaim} macro also puts @code{(cl-eval-when
-(compile load eval) @dots{})} around the declarations so that they will
-be registered at compile-time as well as at run-time.  (This is vital,
-since normally the declarations are meant to influence the way the
-compiler treats the rest of the file that contains the @code{cl-declaim}
-form.)
-@end defmac
 
 @defmac cl-the type form
 @code{cl-the} returns the value of @code{form}, first checking (if
@@ -2697,104 +2673,6 @@ Declarations
 to @code{(mapcar 'car foo)}.
 @end defmac
 
-Each @var{decl-spec} in a @code{cl-proclaim} or @code{cl-declaim}
-should be a list beginning with a symbol that says
-what kind of declaration it is.  This package currently understands
-@code{special}, @code{inline}, @code{notinline}, @code{optimize},
-and @code{warn} declarations.  (The @code{warn} declaration is an
-extension of standard Common Lisp.)  Other Common Lisp declarations,
-such as @code{type} and @code{ftype}, are silently ignored.
-
-@table @code
-@item special
-@c FIXME ?
-Since all variables in Emacs Lisp are ``special'' (in the Common
-Lisp sense), @code{special} declarations are only advisory.  They
-simply tell the byte compiler that the specified
-variables are intentionally being referred to without being
-bound in the body of the function.  The compiler normally emits
-warnings for such references, since they could be typographical
-errors for references to local variables.
-
-In top-level contexts, it is generally better to write
-@code{(defvar @var{var})} than @code{(cl-declaim (special @var{var}))},
-since @code{defvar} makes your intentions clearer.
-
-@item inline
-The @code{inline} @var{decl-spec} lists one or more functions
-whose bodies should be expanded ``in-line'' into calling functions
-whenever the compiler is able to arrange for it.  For example,
-the function @code{cl-acons} is declared @code{inline}
-by this package so that the form @code{(cl-acons @var{key} @var{value}
-@var{alist})} will
-expand directly into @code{(cons (cons @var{key} @var{value}) @var{alist})}
-when it is called in user functions, so as to save function calls.
-
-The following declarations are all equivalent.  Note that the
-@code{defsubst} form is a convenient way to define a function
-and declare it inline all at once.
-
-@example
-(cl-declaim (inline foo bar))
-(cl-eval-when (compile load eval)
-  (cl-proclaim '(inline foo bar)))
-(defsubst foo (@dots{}) @dots{})       ; instead of defun
-@end example
-
-@strong{Please note:}  this declaration remains in effect after the
-containing source file is done.  It is correct to use it to
-request that a function you have defined should be inlined,
-but it is impolite to use it to request inlining of an external
-function.
-
-@item notinline
-The @code{notinline} declaration lists functions which should
-not be inlined after all; it cancels a previous @code{inline}
-declaration.
-
-@item optimize
-This declaration controls how much optimization is performed by
-the compiler.
-
-The word @code{optimize} is followed by any number of lists like
-@code{(speed 3)} or @code{(safety 2)}.  Common Lisp defines several
-optimization ``qualities''; this package ignores all but @code{speed}
-and @code{safety}.  The value of a quality should be an integer from
-0 to 3, with 0 meaning ``unimportant'' and 3 meaning ``very important''.
-The default level for both qualities is 1.
-
-In this package, the @code{speed} quality is tied to the @code{byte-optimize}
-flag, which is set to @code{nil} for @code{(speed 0)} and to
-@code{t} for higher settings; and the @code{safety} quality is
-tied to the @code{byte-compile-delete-errors} flag, which is
-set to @code{nil} for @code{(safety 3)} and to @code{t} for all
-lower settings.  (The latter flag controls whether the compiler
-is allowed to optimize out code whose only side-effect could
-be to signal an error, e.g., rewriting @code{(progn foo bar)} to
-@code{bar} when it is not known whether @code{foo} will be bound
-at run-time.)
-
-Note that even compiling with @code{(safety 0)}, the Emacs
-byte-code system provides sufficient checking to prevent real
-harm from being done.  For example, barring serious bugs in
-Emacs itself, Emacs will not crash with a segmentation fault
-just because of an error in a fully-optimized Lisp program.
-
-The @code{optimize} declaration is normally used in a top-level
-@code{cl-proclaim} or @code{cl-declaim} in a file.
-
-@item warn
-This declaration controls what sorts of warnings are generated
-by the byte compiler.  The word @code{warn} is followed by any
-number of ``warning qualities'', similar in form to optimization
-qualities.  The currently supported warning types are
-@code{redefine}, @code{callargs}, @code{unresolved}, and
-@code{free-vars}; in the current system, a value of 0 will
-disable these warnings and any higher value will enable them.
-See the documentation of the variable @code{byte-compile-warnings}
-for more details.
-@end table
-
 @node Symbols
 @chapter Symbols
 
diff --git a/etc/NEWS b/etc/NEWS
index 763ff2206ac..6f2bb61d1de 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -540,6 +540,12 @@ It is an alias for the 'progn' special-form.
 +++
 *** 'cl-declare' is now obsolete; use 'defvar' instead.
 
++++
+*** 'cl-proclaim' and 'cl-declaim' are now obsolete.
+These rarely used macros are hard to maintain due to relying on hacks,
+and don't provide more than marginal benefits over standard Emacs Lisp
+facilities (such as 'declare').
+
 ** Whitespace
 
 ---
diff --git a/lisp/emacs-lisp/cl-lib.el b/lisp/emacs-lisp/cl-lib.el
index 2bab451dd0c..b59630afc11 100644
--- a/lisp/emacs-lisp/cl-lib.el
+++ b/lisp/emacs-lisp/cl-lib.el
@@ -86,8 +86,8 @@
 
 (require 'macroexp)
 
-(defvar cl--optimize-speed 1)
-(defvar cl--optimize-safety 1)
+(defvar cl--optimize-speed 1 "This variable is deprecated.")
+(defvar cl--optimize-safety 1 "This variable is deprecated.")
 
 ;;;###autoload
 (defvar cl-custom-print-functions nil
@@ -251,6 +251,7 @@ cl--proclaims-deferred
 
 (defun cl-proclaim (spec)
   "Record a global declaration specified by SPEC."
+  (declare (obsolete nil "31.1"))
   (if (fboundp 'cl--do-proclaim) (cl--do-proclaim spec t)
     (push spec cl--proclaims-deferred))
   nil)
@@ -259,6 +260,7 @@ cl-declaim
   "Like `cl-proclaim', but takes any number of unevaluated, unquoted arguments.
 Puts `(cl-eval-when (compile load eval) ...)' around the declarations
 so that they are registered at compile-time as well as run-time."
+  (declare (obsolete nil "31.1"))
   (let ((body (mapcar (lambda (x) `(cl-proclaim ',x)) specs)))
     (if (macroexp-compiling-p) `(cl-eval-when (compile load eval) ,@body)
       `(progn ,@body))))           ; Avoid loading cl-macs.el for cl-eval-when.
diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el
index f95fd65fa5c..faa385810cd 100644
--- a/lisp/emacs-lisp/eieio-core.el
+++ b/lisp/emacs-lisp/eieio-core.el
@@ -82,7 +82,10 @@ eieio-default-superclass
 (progn
   ;; Arrange for field access not to bother checking if the access is indeed
   ;; made to an eieio--class object.
-  (eval-when-compile (cl-declaim (optimize (safety 0))))
+  (eval-when-compile
+    ;; FIXME: Make this fast without using `cl--optimize-safety'.
+    (setq cl--optimize-safety 0)
+    (setq byte-compile-delete-errors t))
 
 (cl-defstruct (eieio--class
                (:constructor nil)
@@ -101,12 +104,10 @@ eieio-default-superclass
   options ;; storage location of tagged class option
           ; Stored outright without modifications or stripping
   )
-  ;; Set it back to the default value.  NOTE: Using the default
-  ;; `safety' value does NOT give the default
-  ;; `byte-compile-delete-errors' value.  Therefore limit this (and
-  ;; the above `cl-declaim') to compile time so that we don't affect
-  ;; code which only loads this library.
-  (eval-when-compile (cl-declaim (optimize (safety 1)))))
+  ;; Set it back to the default value.
+  (eval-when-compile
+    (setq cl--optimize-safety 1)
+    (setq byte-compile-delete-errors nil)))

 (eval-and-compile
-- 
2.48.1