summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp
diff options
context:
space:
mode:
authorTino Calancha <tino.calancha@gmail.com>2018-01-08 19:11:20 +0900
committerTino Calancha <tino.calancha@gmail.com>2018-01-08 19:15:28 +0900
commita0365437c9ee308ad7978e436631020f513b25e7 (patch)
treec6164968de607fd2bfd37c32983fe9a4be0eceba /lisp/emacs-lisp
parent1daac66a6eedbcbfa32ab920b5c579872d989517 (diff)
downloademacs-a0365437c9ee308ad7978e436631020f513b25e7.tar.gz
emacs-a0365437c9ee308ad7978e436631020f513b25e7.tar.bz2
emacs-a0365437c9ee308ad7978e436631020f513b25e7.zip
cl-loop: Add missing guard condition
Consider the expansion of `cl-loop' with a `for' clause and more than one internal variables, X, Y, processed in parallel. Each step updates X and Y right after update the loop variable, K; if either X or Y depend on K, then some forms of the body are evaluated with the wrong K (Bug#29799). For instance, consider the following code: (cl-loop for k below 2 for x = (progn (message "k = %d" k) 1) and y = 1) This code should show in *Messages*: k = 0 k = 1 Instead, the code shows: k = 0 k = 1 k = 2 To prevent this we must ensure that the loop condition is still satisfied right after update the loop variable. In the macro expansion of the example above, right after: (setq k (+ k 1)) evaluate the rest of the body forms iif the condition (< k 2) is still valid. * lisp/emacs-lisp/cl-macs.el (cl--loop-guard-cond): New variable. (cl--parse-loop-clause): Set it non-nil if the loop contains a for/as clause. (cl-loop): After update the loop variable, evaluate the remaining of the body forms just if the loop condition is still valid (Bug#29799). * test/lisp/emacs-lisp/cl-macs-tests.el (cl-macs-loop-for-as-equals-and): New test.
Diffstat (limited to 'lisp/emacs-lisp')
-rw-r--r--lisp/emacs-lisp/cl-macs.el32
1 files changed, 25 insertions, 7 deletions
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 9af014cf8e9..43eb4261162 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -892,7 +892,7 @@ This is compatible with Common Lisp, but note that `defun' and
(defvar cl--loop-name)
(defvar cl--loop-result) (defvar cl--loop-result-explicit)
(defvar cl--loop-result-var) (defvar cl--loop-steps)
-(defvar cl--loop-symbol-macs)
+(defvar cl--loop-symbol-macs) (defvar cl--loop-guard-cond)
(defun cl--loop-set-iterator-function (kind iterator)
(if cl--loop-iterator-function
@@ -961,7 +961,7 @@ For more details, see Info node `(cl)Loop Facility'.
(cl--loop-accum-var nil) (cl--loop-accum-vars nil)
(cl--loop-initially nil) (cl--loop-finally nil)
(cl--loop-iterator-function nil) (cl--loop-first-flag nil)
- (cl--loop-symbol-macs nil))
+ (cl--loop-symbol-macs nil) (cl--loop-guard-cond nil))
;; Here is more or less how those dynbind vars are used after looping
;; over cl--parse-loop-clause:
;;
@@ -996,7 +996,24 @@ For more details, see Info node `(cl)Loop Facility'.
(list (or cl--loop-result-explicit
cl--loop-result))))
(ands (cl--loop-build-ands (nreverse cl--loop-body)))
- (while-body (nconc (cadr ands) (nreverse cl--loop-steps)))
+ (while-body
+ (nconc
+ (cadr ands)
+ (if (or (not cl--loop-guard-cond) (not cl--loop-first-flag))
+ (nreverse cl--loop-steps)
+ ;; Right after update the loop variable ensure that the loop
+ ;; condition, i.e. (car ands), is still satisfied; otherwise,
+ ;; set `cl--loop-first-flag' nil and skip the remaining
+ ;; body forms (#Bug#29799).
+ ;;
+ ;; (last cl--loop-steps) updates the loop var
+ ;; (car (butlast cl--loop-steps)) sets `cl--loop-first-flag' nil
+ ;; (nreverse (cdr (butlast cl--loop-steps))) are the
+ ;; remaining body forms.
+ (append (last cl--loop-steps)
+ `((and ,(car ands)
+ ,@(nreverse (cdr (butlast cl--loop-steps)))))
+ `(,(car (butlast cl--loop-steps)))))))
(body (append
(nreverse cl--loop-initially)
(list (if cl--loop-iterator-function
@@ -1506,10 +1523,11 @@ For more details, see Info node `(cl)Loop Facility'.
,(cl--loop-let (nreverse loop-for-sets) 'setq ands)
t)
cl--loop-body))
- (if loop-for-steps
- (push (cons (if ands 'cl-psetq 'setq)
- (apply 'append (nreverse loop-for-steps)))
- cl--loop-steps))))
+ (when loop-for-steps
+ (setq cl--loop-guard-cond t)
+ (push (cons (if ands 'cl-psetq 'setq)
+ (apply 'append (nreverse loop-for-steps)))
+ cl--loop-steps))))
((eq word 'repeat)
(let ((temp (make-symbol "--cl-var--")))