Don't rewrite (nconc X nil) -> X for any X (bug#63103)

Since the last cdr of a non-terminal argument to `nconc` is
overwritten no matter its value:

  (nconc (cons 1 2) nil) => (1)

a terminating nil arg cannot just be eliminated unconditionally.

* lisp/emacs-lisp/byte-opt.el (byte-optimize-nconc):
Only eliminate a terminal nil arg to `nconc` if preceded by
a nonempty proper list.  Right now we only bother to prove this
for `(list ...)`, so that

  (nconc (list 1 2 3) nil) -> (list 1 2 3)

* test/lisp/emacs-lisp/bytecomp-tests.el
(bytecomp-tests--test-cases): Add test cases.
This commit is contained in:
Mattias Engdegård 2023-04-27 12:38:58 +02:00
parent 521386f920
commit 1dcb737405
2 changed files with 20 additions and 1 deletions

View file

@ -1531,7 +1531,16 @@ See Info node `(elisp) Integer Basics'."
(prev (car newargs)))
(cond
;; Elide null args.
((null arg) (loop (cdr args) newargs))
((and (null arg)
;; Don't elide a terminal nil unless preceded by
;; a nonempty proper list, since that will have
;; its last cdr forced to nil.
(or (cdr args)
;; FIXME: prove the 'nonempty proper list' property
;; for more forms than just `list', such as
;; `append', `mapcar' etc.
(eq 'list (car-safe (car newargs)))))
(loop (cdr args) newargs))
;; Merge consecutive `list' args.
((and (eq (car-safe arg) 'list)
(eq (car-safe prev) 'list))

View file

@ -766,6 +766,16 @@ inner loops respectively."
((eq x 2) (setq y 'c)))
(list x y)))))
(mapcar fn (bytecomp-test-identity '(0 1 2 3 10 11))))
;; `nconc' nil arg elimination
(nconc (list 1 2 3 4) nil)
(nconc (list 1 2 3 4) nil nil)
(let ((x (cons 1 (cons 2 (cons 3 4)))))
(nconc x nil))
(let ((x (cons 1 (cons 2 (cons 3 4)))))
(nconc x nil nil))
(let ((x (cons 1 (cons 2 (cons 3 4)))))
(nconc nil x nil (list 5 6) nil))
)
"List of expressions for cross-testing interpreted and compiled code.")