Skip to content

Fix control flow when the loop contains NIL blocks

Steve Losh requested to merge sjl/iterate:fix-nil-block-returns into master

Control flow clauses that return from an iterate form without executing the epilogue code (e.g. never and leave) currently do so by expanding into (return-from ,*block-name*). For iterate forms without a user-provided name, the block will be named nil. This causes problems when used with other Common Lisp looping constructs such as dolist:

(iterate
  (for i :from 0 :below 10)
  (dolist (x '(5 10 15))
    (never (= i x))))
; => T, but should be NIL

This patch adds a separate *loop-name* variable that will be bound to a gensym for NIL-named blocks and makes the various epilogue-skipping code refer to that instead of *block-name*. Iterate forms without an explicit user-provided name will still have a separate (block nil ...) wrapping so users can still (return ...) as before.

Before (simplified):

(iterate (leave))
; ==> (block nil (return-from nil))

(iterate foo (leave))
; ==> (block foo (return-from nil))

After:

(iterate (leave))
; ==> (block nil (block #:ITERATE123 (return-from #:ITERATE123)))

(iterate foo (leave))
; ==> (block foo (return-from foo))

Merge request reports