Wanted: Warn when a LAMBDA capturing the binding of an iteration variable escapes the dynamic extent of the iteration that introduced it
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
SBCL |
Triaged
|
Wishlist
|
Unassigned |
Bug Description
I believe these 4 pieces of code should result in a compile-time WARNING, because their effect is specified to be implementation-
(mapcar #'funcall
(let (list)
(dolist (i '(0 1 2) (nreverse list))
(push (lambda () i) list))))
;; This is the result we probably wanted, but another implementation can return something else!
;; This is a portability hazard. I have tons and tons of old code that does this.
;; Some SBCL internals might rely on DOLIST's fresh binding, so maybe the WARNING should be disabled in SBCL's implementation code?
=> (0 1 2)
(mapcar #'funcall
(let (list)
(dotimes (i 3 (nreverse list))
(push (lambda () i) list))))
;; One might seriously waste hours searching for the bug in a real coding scenario
;; if he wasn't aware that a fresh binding is not necessarily created on each new iteration.
;; I know I did, some time ago! Even more puzzling because DOLIST just happens to have the intended effect on SBCL.
=> (3 3 3)
(mapcar #'funcall
(loop for i below 3
=> (3 3 3)
(let (previous list)
(dotimes (i 3 (progn (push (funcall previous) list)
(when previous
(push (funcall previous) list))
(setf previous (lambda () i))))
;; Notice the nice off-by-one...
;; This code is much less likely to get written, but it illustrates that the problem is when the LAMBDA capturing the iteration variable escapes the iteration where it was introduced, not just when it escapes the iteration construct entirely.
=> (1 2 3)
So, a warning a bit like this would be nice:
"WARNING: Lambda #<FUNCTION (LAMBDA #) {AB13235}> captures the binding of iteration variable I and escapes the dynamic extent of the DOLIST iteration that introduced it. The standard specifies that:
"It is implementation-
See http://
Jean-Philippe Paradis <email address hidden> writes:
> I believe these 4 pieces of code should result in a compile-time
> WARNING, because their effect is specified to be implementation-
> dependent and won't do what one probably wants across all
> implementations.
I don't. I think there's value for sbcl to distinguish some unportable -dependent" is a long, long way away
code scenarios, but "implementation
From "undefined consequences", which is what has normally triggered a
compile-time WARNING. Consider: what if you actually /want/ to write
this code? How do you tell the system "yes, I know what I'm doing?".
(This is in contrast with the "undefined consequences" situation, where
there is no way in fact that the writer of code causing undefined
consequences really knows what they are doing).
Christophe