dynamic-extent scope too wide for nested closures

Bug #1761306 reported by Andrzej Walczak on 2018-04-04
10
This bug affects 2 people
Affects Status Importance Assigned to Milestone
SBCL
Undecided
Unassigned

Bug Description

code:

(defun foo (&rest args)
  (sb-thread:make-thread
   (lambda ()
     (flet ((bar () (apply #'format t args)))
       (declare (dynamic-extent #'bar))
       (bar)))))

The function BAR is declared dynamic-extent within the LAMBDA,
yet it ends up compiled like the following code instead:

(defun foo2 (&rest args)
  (flet ((bar () (apply #'format t args)))
    (declare (dynamic-extent #'bar))
    (sb-thread:make-thread #'bar)))

Which will usually lead to an "undefined behavior".

The problem is amplified because the dynamic-extent declaration maybe
inserted by any macro where e.g. the &body parameter representing function
usually does not need to exhibit an indefinite extent property.
Thus the user of such a macro will not know about the hidden gotchas.

Therefore we suggest that the current SBCL behavior is less then optimal
as it may lead to unexpected and difficult to trace errors.

Another example:

(defun foo (&rest args)
  (lambda ()
    (flet ((bar () (apply #'format t args)))
      (declare (dynamic-extent #'bar))
      (bar))))

(defparameter foo (foo "hello"))

(funcall foo) => random crash

summary: - dynamic-extent scope to wide
+ dynamic-extent scope too wide for nested closures
Stas Boukarev (stassats) wrote :

The dynamic-extent is applied to the &rest list, not the function, and then it's used out of extent.

Changed in sbcl:
status: New → Invalid

I am sorry to reopen this.
I believe that propagating DX property outside of a non-DX closure is an error.

The &rest args list here is an example of things that get into the closure of BAR - it was just used as an example. Having outside scope variables become DX is contrary to the referential transparency check.

code2:

(defun thread-loop (count bar)
  (loop :repeat count :do (funcall bar)))

(defmacro spaw (count &body body)
 `(sb-thread:make-thread
   (lambda ()
     (flet ((bar () ,@body))
       ;; The assumption here is that BAR is local to
       ;; the above LAMBDA and it will not be called outside DX.
       (declare (dynamic-extent #'bar))
       (thread-loop ,count #'bar)))))

(defun foo (&rest args)
  ;; ARGS not declare here as DX.
  (spawn 10 (apply #'format t args)))

Changed in sbcl:
status: Invalid → New
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers