cmp: fix closure type for local functions calling closures

When a local function calls a closure it has to be a closure too. Thus
when updating the closure type for a function f, we have to possibly
update also all functions referencing f.

Fixes #545.
This commit is contained in:
Marius Gerbershagen 2020-03-18 22:00:57 +01:00
parent 1d7551c773
commit a9065d1d8e
2 changed files with 25 additions and 1 deletions

View file

@ -151,13 +151,14 @@
;; This recursive algorithm is guaranteed to stop when functions
;; do not change.
(let ((new-type (compute-closure-type fun))
(to-be-updated (fun-child-funs fun)))
to-be-updated)
;; Same type
(when (eq new-type old-type)
(return-from update-fun-closure-type nil))
(when (fun-global fun)
(cmpnote "Function ~A is global but is closed over some variables.~%~{~A ~}"
(fun-name fun) (mapcar #'var-name (fun-referenced-vars fun))))
(setf to-be-updated (append (fun-child-funs fun) (fun-referencing-funs fun)))
(setf (fun-closure fun) new-type)
;; All external, non-global variables become of type closure
(when (eq new-type 'CLOSURE)

View file

@ -1606,3 +1606,26 @@
(check-fn (make-fn (1+ si::c-arguments-limit)))
(check-fn (make-fn (1- si::c-arguments-limit)))
(check-fn (make-fn si::c-arguments-limit))))
;;; Date 2020-03-18
;;; URL: https://gitlab.com/embeddable-common-lisp/ecl/issues/545
;;; Description
;;;
;;; The closure type for local functions calling global closures was
;;; not determined correctly to also be a global closure.
(test cmp.0075.local-fun.closure-type
(ext:with-clean-symbols (*function*)
(defvar *function*)
(let ((result
(funcall
(compile nil
(lambda (b)
(flet ((%f10 () b))
(flet ((%f4 () (%f10)))
(incf b)
(setf *function* #'%f10) ; makes a global
; closure out of %f10
(%f4)))))
3)))
(is (eq result 4))
(is (eq (funcall *function*) 4)))))