aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2022-06-03 20:31:10 +0200
committerMattias EngdegÄrd2022-06-14 20:19:59 +0200
commit6825e5686a4bf21f5d5a0ae1af889097cfa2f597 (patch)
treea296141574d6ac3bc085df708d3feeb817f83204
parent175bc8e5a53740432c844b5aae1981d4f47c96f7 (diff)
downloademacs-6825e5686a4bf21f5d5a0ae1af889097cfa2f597.tar.gz
emacs-6825e5686a4bf21f5d5a0ae1af889097cfa2f597.zip
Normalise setq during macro-expansion
Early normalisation of setq during macroexpand-all allows later stages, cconv, byte-opt and codegen, to be simplified and duplicated checks to be eliminated. * lisp/emacs-lisp/macroexp.el (macroexp--expand-all): Normalise all setq forms to a sequence of (setq VAR EXPR). Emit warnings if necessary. * lisp/emacs-lisp/cconv.el (cconv-convert, cconv-analyze-form): * lisp/emacs-lisp/byte-opt.el (byte-optimize-form-code-walker): * lisp/emacs-lisp/bytecomp.el (byte-compile-setq): Simplify. * test/lisp/emacs-lisp/bytecomp-tests.el: Adapt and add tests. * test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-nonvariable.el; * test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-odd.el: New files.
-rw-r--r--lisp/emacs-lisp/byte-opt.el41
-rw-r--r--lisp/emacs-lisp/bytecomp.el26
-rw-r--r--lisp/emacs-lisp/cconv.el47
-rw-r--r--lisp/emacs-lisp/macroexp.el48
-rw-r--r--test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-nonvariable.el3
-rw-r--r--test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-odd.el3
-rw-r--r--test/lisp/emacs-lisp/bytecomp-tests.el8
7 files changed, 101 insertions, 75 deletions
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 69795f9c112..0e10e332b29 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -463,32 +463,21 @@ for speeding up processing.")
463 ;; is a *value* and shouldn't appear in the car. 463 ;; is a *value* and shouldn't appear in the car.
464 (`((closure . ,_) . ,_) form) 464 (`((closure . ,_) . ,_) form)
465 465
466 (`(setq . ,args) 466 (`(setq ,var ,expr)
467 (let ((var-expr-list nil)) 467 (let ((lexvar (assq var byte-optimize--lexvars))
468 (while args 468 (value (byte-optimize-form expr nil)))
469 (unless (and (consp args) 469 (when lexvar
470 (symbolp (car args)) (consp (cdr args))) 470 (setcar (cdr lexvar) t) ; Mark variable to be kept.
471 (byte-compile-warn-x form "malformed setq form: %S" form)) 471 (setcdr (cdr lexvar) nil) ; Inhibit further substitution.
472 (let* ((var (car args)) 472
473 (expr (cadr args)) 473 (when (memq var byte-optimize--aliased-vars)
474 (lexvar (assq var byte-optimize--lexvars)) 474 ;; Cancel aliasing of variables aliased to this one.
475 (value (byte-optimize-form expr nil))) 475 (dolist (v byte-optimize--lexvars)
476 (when lexvar 476 (when (eq (nth 2 v) var)
477 (setcar (cdr lexvar) t) ; Mark variable to be kept. 477 ;; V is bound to VAR but VAR is now mutated:
478 (setcdr (cdr lexvar) nil) ; Inhibit further substitution. 478 ;; cancel aliasing.
479 479 (setcdr (cdr v) nil)))))
480 (when (memq var byte-optimize--aliased-vars) 480 `(,fn ,var ,value)))
481 ;; Cancel aliasing of variables aliased to this one.
482 (dolist (v byte-optimize--lexvars)
483 (when (eq (nth 2 v) var)
484 ;; V is bound to VAR but VAR is now mutated:
485 ;; cancel aliasing.
486 (setcdr (cdr v) nil)))))
487
488 (push var var-expr-list)
489 (push value var-expr-list))
490 (setq args (cddr args)))
491 (cons fn (nreverse var-expr-list))))
492 481
493 (`(defvar ,(and (pred symbolp) name) . ,rest) 482 (`(defvar ,(and (pred symbolp) name) . ,rest)
494 (let ((optimized-rest (and rest 483 (let ((optimized-rest (and rest
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index ab21fba8a27..1f868d2217c 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -4225,25 +4225,13 @@ This function is never called when `lexical-binding' is nil."
4225(byte-defop-compiler-1 quote) 4225(byte-defop-compiler-1 quote)
4226 4226
4227(defun byte-compile-setq (form) 4227(defun byte-compile-setq (form)
4228 (let* ((args (cdr form)) 4228 (cl-assert (= (length form) 3)) ; normalised in macroexp
4229 (len (length args))) 4229 (let ((var (nth 1 form))
4230 (if (= (logand len 1) 1) 4230 (expr (nth 2 form)))
4231 (progn 4231 (byte-compile-form expr)
4232 (byte-compile-report-error 4232 (unless byte-compile--for-effect
4233 (format-message 4233 (byte-compile-out 'byte-dup 0))
4234 "missing value for `%S' at end of setq" (car (last args)))) 4234 (byte-compile-variable-set var)
4235 (byte-compile-form
4236 `(signal 'wrong-number-of-arguments '(setq ,len))
4237 byte-compile--for-effect))
4238 (if args
4239 (while args
4240 (byte-compile-form (car (cdr args)))
4241 (or byte-compile--for-effect (cdr (cdr args))
4242 (byte-compile-out 'byte-dup 0))
4243 (byte-compile-variable-set (car args))
4244 (setq args (cdr (cdr args))))
4245 ;; (setq), with no arguments.
4246 (byte-compile-form nil byte-compile--for-effect)))
4247 (setq byte-compile--for-effect nil))) 4235 (setq byte-compile--for-effect nil)))
4248 4236
4249(byte-defop-compiler-1 set-default) 4237(byte-defop-compiler-1 set-default)
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index 1a501f50bfc..b12f1db677e 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -555,29 +555,19 @@ places where they originally did not directly appear."
555 `(,(car form) ,(cconv-convert form1 env extend) 555 `(,(car form) ,(cconv-convert form1 env extend)
556 :fun-body ,(cconv--convert-function () body env form1))) 556 :fun-body ,(cconv--convert-function () body env form1)))
557 557
558 (`(setq . ,forms) ; setq special form 558 (`(setq ,var ,expr)
559 (if (= (logand (length forms) 1) 1) 559 (let ((var-new (or (cdr (assq var env)) var))
560 ;; With an odd number of args, let bytecomp.el handle the error. 560 (value (cconv-convert expr env extend)))
561 form 561 (pcase var-new
562 (let ((prognlist ())) 562 ((pred symbolp) `(,(car form) ,var-new ,value))
563 (while forms 563 (`(car-safe ,iexp) `(setcar ,iexp ,value))
564 (let* ((sym (pop forms)) 564 ;; This "should never happen", but for variables which are
565 (sym-new (or (cdr (assq sym env)) sym)) 565 ;; mutated+captured+unused, we may end up trying to `setq'
566 (value (cconv-convert (pop forms) env extend))) 566 ;; on a closed-over variable, so just drop the setq.
567 (push (pcase sym-new 567 (_ ;; (byte-compile-report-error
568 ((pred symbolp) `(,(car form) ,sym-new ,value)) 568 ;; (format "Internal error in cconv of (setq %s ..)"
569 (`(car-safe ,iexp) `(setcar ,iexp ,value)) 569 ;; sym-new))
570 ;; This "should never happen", but for variables which are 570 value))))
571 ;; mutated+captured+unused, we may end up trying to `setq'
572 ;; on a closed-over variable, so just drop the setq.
573 (_ ;; (byte-compile-report-error
574 ;; (format "Internal error in cconv of (setq %s ..)"
575 ;; sym-new))
576 value))
577 prognlist)))
578 (if (cdr prognlist)
579 `(progn . ,(nreverse prognlist))
580 (car prognlist)))))
581 571
582 (`(,(and (or 'funcall 'apply) callsym) ,fun . ,args) 572 (`(,(and (or 'funcall 'apply) callsym) ,fun . ,args)
583 ;; These are not special forms but we treat them separately for the needs 573 ;; These are not special forms but we treat them separately for the needs
@@ -751,14 +741,13 @@ This function does not return anything but instead fills the
751 (cconv-analyze-form (cadr (pop body-forms)) env)) 741 (cconv-analyze-form (cadr (pop body-forms)) env))
752 (cconv--analyze-function vrs body-forms env form)) 742 (cconv--analyze-function vrs body-forms env form))
753 743
754 (`(setq . ,forms) 744 (`(setq ,var ,expr)
755 ;; If a local variable (member of env) is modified by setq then 745 ;; If a local variable (member of env) is modified by setq then
756 ;; it is a mutated variable. 746 ;; it is a mutated variable.
757 (while forms 747 (let ((v (assq var env))) ; v = non nil if visible
758 (let ((v (assq (car forms) env))) ; v = non nil if visible 748 (when v
759 (when v (setf (nth 2 v) t))) 749 (setf (nth 2 v) t)))
760 (cconv-analyze-form (cadr forms) env) 750 (cconv-analyze-form expr env))
761 (setq forms (cddr forms))))
762 751
763 (`((lambda . ,_) . ,_) ; First element is lambda expression. 752 (`((lambda . ,_) . ,_) ; First element is lambda expression.
764 (byte-compile-warn-x 753 (byte-compile-warn-x
diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el
index 51c6e8e0ca2..bae303c213c 100644
--- a/lisp/emacs-lisp/macroexp.el
+++ b/lisp/emacs-lisp/macroexp.el
@@ -369,6 +369,54 @@ Assumes the caller has bound `macroexpand-all-environment'."
369 (macroexp--all-forms body)) 369 (macroexp--all-forms body))
370 (cdr form)) 370 (cdr form))
371 form))) 371 form)))
372 (`(setq ,(and var (pred symbolp)
373 (pred (not booleanp)) (pred (not keywordp)))
374 ,expr)
375 ;; Fast path for the setq common case.
376 (let ((new-expr (macroexp--expand-all expr)))
377 (if (eq new-expr expr)
378 form
379 `(,fn ,var ,new-expr))))
380 (`(setq . ,args)
381 ;; Normalise to a sequence of (setq SYM EXPR).
382 ;; Malformed code is translated to code that signals an error
383 ;; at run time.
384 (let ((nargs (length args)))
385 (if (/= (logand nargs 1) 0)
386 (macroexp-warn-and-return
387 "odd number of arguments in `setq' form"
388 `(signal 'wrong-number-of-arguments '(setq ,nargs))
389 nil 'compile-only fn)
390 (let ((assignments nil))
391 (while (consp (cdr-safe args))
392 (let* ((var (car args))
393 (expr (cadr args))
394 (new-expr (macroexp--expand-all expr))
395 (assignment
396 (if (and (symbolp var)
397 (not (booleanp var)) (not (keywordp var)))
398 `(,fn ,var ,new-expr)
399 (macroexp-warn-and-return
400 (format-message "attempt to set %s `%s'"
401 (if (symbolp var)
402 "constant"
403 "non-variable")
404 var)
405 (cond
406 ((keywordp var)
407 ;; Accept `(setq :a :a)' for compatibility.
408 `(if (eq ,var ,new-expr)
409 ,var
410 (signal 'setting-constant (list ',var))))
411 ((symbolp var)
412 `(signal 'setting-constant (list ',var)))
413 (t
414 `(signal 'wrong-type-argument
415 (list 'symbolp ',var))))
416 nil 'compile-only var))))
417 (push assignment assignments))
418 (setq args (cddr args)))
419 (cons 'progn (nreverse assignments))))))
372 (`(,(and fun `(lambda . ,_)) . ,args) 420 (`(,(and fun `(lambda . ,_)) . ,args)
373 ;; Embedded lambda in function position. 421 ;; Embedded lambda in function position.
374 ;; If the byte-optimizer is loaded, try to unfold this, 422 ;; If the byte-optimizer is loaded, try to unfold this,
diff --git a/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-nonvariable.el b/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-nonvariable.el
new file mode 100644
index 00000000000..5a56913cd9b
--- /dev/null
+++ b/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-nonvariable.el
@@ -0,0 +1,3 @@
1;;; -*- lexical-binding: t -*-
2(defun foo ()
3 (setq (a) nil))
diff --git a/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-odd.el b/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-odd.el
new file mode 100644
index 00000000000..9ce80de08cd
--- /dev/null
+++ b/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-setq-odd.el
@@ -0,0 +1,3 @@
1;;; -*- lexical-binding: t -*-
2(defun foo (a b)
3 (setq a 1 b))
diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el
index 27098d0bb1c..9abc17a1c41 100644
--- a/test/lisp/emacs-lisp/bytecomp-tests.el
+++ b/test/lisp/emacs-lisp/bytecomp-tests.el
@@ -951,11 +951,17 @@ byte-compiled. Run with dynamic binding."
951 "let-bind nonvariable") 951 "let-bind nonvariable")
952 952
953(bytecomp--define-warning-file-test "warn-variable-set-constant.el" 953(bytecomp--define-warning-file-test "warn-variable-set-constant.el"
954 "variable reference to constant") 954 "attempt to set constant")
955 955
956(bytecomp--define-warning-file-test "warn-variable-set-nonvariable.el" 956(bytecomp--define-warning-file-test "warn-variable-set-nonvariable.el"
957 "variable reference to nonvariable") 957 "variable reference to nonvariable")
958 958
959(bytecomp--define-warning-file-test "warn-variable-setq-nonvariable.el"
960 "attempt to set non-variable")
961
962(bytecomp--define-warning-file-test "warn-variable-setq-odd.el"
963 "odd number of arguments")
964
959(bytecomp--define-warning-file-test 965(bytecomp--define-warning-file-test
960 "warn-wide-docstring-autoload.el" 966 "warn-wide-docstring-autoload.el"
961 "autoload .foox. docstring wider than .* characters") 967 "autoload .foox. docstring wider than .* characters")