From: Pip Cet Date: Sun, 27 Sep 2020 14:59:00 +0000 (+0200) Subject: Handle single-argument `apply' consistently (bug#40968) X-Git-Tag: emacs-28.0.90~5849 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=433b6fc53dc9511077ed3a8c1ad130196dedbb55;p=emacs.git Handle single-argument `apply' consistently (bug#40968) * src/eval.c (Fapply): Handle (apply nil) without crashing. Document single-argument form. * lisp/emacs-lisp/byte-opt.el (byte-optimize-apply): Don't attempt to optimize single-argument apply. * doc/lispref/functions.texi (Calling Functions): Document single-argument apply. Provide example (bug#40968). --- diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index 26b212d05eb..e8e22078d9b 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -762,6 +762,11 @@ arguments, rather than a single list. We say that @code{apply} @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. Passing two or +more arguments will be faster. + @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 @@ -789,6 +794,11 @@ primitive function; special forms and macros do not make sense in (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 8a6c0b9a7fa..65e4e446266 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -1044,19 +1044,22 @@ (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 not worth optimizing (bug#40968) + 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 5d3c32326db..c34c11828c5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2371,6 +2371,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) @@ -2381,6 +2383,10 @@ usage: (apply FUNCTION &rest ARGUMENTS) */) Lisp_Object fun = args[0]; USE_SAFE_ALLOCA; + if (nargs == 1) + /* Special case: FUN is really a list of (FUNCTION . ARGS). */ + return CALLN (Fapply, CAR (fun), CDR (fun)); + ptrdiff_t numargs = list_length (spread_arg); if (numargs == 0)