Subject: bug#40968: 28.0.50; (apply nil)

From: Pip Cet <[email-masked]>
Date: Wed, 6 May 2020 17:54:23 +0000

On Wed, May 6, 2020 at 2:04 PM Eli Zaretskii <[email-masked]> wrote:
> 'apply' is a very popular function, and is used very widely.  I won't
> be surprised if there were more of these usage cases that would be
> broken by such a change in the signature.

> Can we instead identify the problematic usage and signal an error?

Yes. What we also have to do is fix the documentation, and fix the
byte optimizer.

Here's a first suggestion. I'd particularly appreciate hints on better
wording for the documentation.
From fa819045766f50c55f9bb588ecf7e0bef6a56d41 Mon Sep 17 00:00:00 2001
From: Pip Cet <[email-masked]>
Date: Wed, 6 May 2020 17:46:56 +0000
Subject: [PATCH] Handle single-argument `apply' consistently (bug#40968)

---
 doc/lispref/functions.texi  |  9 +++++++++
 lisp/emacs-lisp/byte-opt.el | 29 ++++++++++++++++-------------
 src/eval.c                  |  5 +++++
 3 files changed, 30 insertions(+), 13 deletions(-)

diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index bc8ec0ef1b..054bcc169e 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -761,6 +761,10 @@ Calling Functions
 @dfn{spreads} this list so that each individual element becomes an
 argument.
 
+@code{apply} with a single argument is special: the first element of
+the argument, which must be a non-empty list, is called as a function
+with the remaining elements as individual arguments.
+
 @code{apply} returns the result of calling @var{function}.  As with
 @code{funcall}, @var{function} must either be a Lisp function or a
 primitive function; special forms and macros do not make sense in
@@ -788,6 +792,11 @@ Calling Functions
 (apply 'append '((a b c) nil (x y z) nil))
      @result{} (a b c x y z)
 @end group
+
+@group
+(apply '(+ 3 4))
+     @result{} 7
+@end group
 @end example
 
 For an interesting example of using @code{apply}, see @ref{Definition
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 4f72251aed..32e53bb7e9 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -1098,19 +1098,22 @@ byte-optimize-funcall
 (defun byte-optimize-apply (form)
   ;; If the last arg is a literal constant, turn this into a funcall.
   ;; The funcall optimizer can then transform (funcall 'foo ...) -> (foo ...).
-  (let ((fn (nth 1 form))
-	(last (nth (1- (length form)) form))) ; I think this really is fastest
-    (or (if (or (null last)
-		(eq (car-safe last) 'quote))
-	    (if (listp (nth 1 last))
-		(let ((butlast (nreverse (cdr (reverse (cdr (cdr form)))))))
-		  (nconc (list 'funcall fn) butlast
-			 (mapcar (lambda (x) (list 'quote x)) (nth 1 last))))
-	      (byte-compile-warn
-	       "last arg to apply can't be a literal atom: `%s'"
-	       (prin1-to-string last))
-	      nil))
-	form)))
+  (if (= (length form) 2)
+      ;; single-argument `apply' is special (bug#40968)
+      (byte-optimize-apply `(apply #'funcall ,(cadr form)))
+    (let ((fn (nth 1 form))
+	  (last (nth (1- (length form)) form))) ; I think this really is fastest
+      (or (if (or (null last)
+		  (eq (car-safe last) 'quote))
+	      (if (listp (nth 1 last))
+		  (let ((butlast (nreverse (cdr (reverse (cdr (cdr form)))))))
+		    (nconc (list 'funcall fn) butlast
+			   (mapcar (lambda (x) (list 'quote x)) (nth 1 last))))
+	        (byte-compile-warn
+	         "last arg to apply can't be a literal atom: `%s'"
+	         (prin1-to-string last))
+	        nil))
+	  form))))
 
 (put 'funcall 'byte-optimizer 'byte-optimize-funcall)
 (put 'apply   'byte-optimizer 'byte-optimize-apply)
diff --git a/src/eval.c b/src/eval.c
index 014905ce6d..77f54ad7b1 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2361,6 +2361,8 @@ eval_sub (Lisp_Object form)
 DEFUN ("apply", Fapply, Sapply, 1, MANY, 0,
        doc: /* Call FUNCTION with our remaining args, using our last arg as list of args.
 Then return the value FUNCTION returns.
+With a single argument, call the argument's first element using the
+other elements as args.
 Thus, (apply \\='+ 1 2 \\='(3 4)) returns 10.
 usage: (apply FUNCTION &rest ARGUMENTS)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
@@ -2373,6 +2375,9 @@ DEFUN ("apply", Fapply, Sapply, 1, MANY, 0,
 
   ptrdiff_t numargs = list_length (spread_arg);
 
+  if (numargs == 0 && nargs == 1)
+    wrong_type_argument (Qconsp, spread_arg);
+
   if (numargs == 0)
     return Ffuncall (nargs - 1, args);
   else if (numargs == 1)
-- 
2.26.2