~ chicken-core (chicken-5) 05d663cccb4ea37b3f86f5020dbde8c29613a591
commit 05d663cccb4ea37b3f86f5020dbde8c29613a591 Author: Peter Bex <peter.bex@xs4all.nl> AuthorDate: Sun Dec 29 16:57:14 2013 +0100 Commit: Christian Kellermann <ckeen@pestilenz.org> CommitDate: Fri Jan 3 20:27:55 2014 +0100 Fix for #1068 (2): don't allow captured lambdas to get replaced. This caused issues with letrec-like constructs: by replacing variables with complex lambda expressions lexical scoping would be broken and references to variables could be moved around to a location where the variable was out of scope. More generally, the optimization which replaces variables completely ignores all scoping rules, which can cause issues if the values being moved about refer to other variables in the same scope. For unknown reasons, this hasn't caused issues in other situations yet. Signed-off-by: Christian Kellermann <ckeen@pestilenz.org> diff --git a/compiler.scm b/compiler.scm index f356eaf0..6a84060b 100644 --- a/compiler.scm +++ b/compiler.scm @@ -2169,14 +2169,15 @@ (when (eq? '##core#variable (node-class value)) (let* ([name (first (node-parameters value))] [nrefs (get db name 'references)] ) - (when (or (and (not (get db name 'unknown)) (get db name 'value)) - (and (not (get db name 'captured)) - nrefs - (= 1 (length nrefs)) - (not assigned) - (not (get db name 'assigned)) - (or (not (variable-visible? name)) - (not (get db name 'global))) ) ) + (when (and (not captured) + (or (and (not (get db name 'unknown)) (get db name 'value)) + (and (not (get db name 'captured)) + nrefs + (= 1 (length nrefs)) + (not assigned) + (not (get db name 'assigned)) + (or (not (variable-visible? name)) + (not (get db name 'global))) ) )) (quick-put! plist 'replacable name) (put! db name 'replacing #t) ) ) ) ) diff --git a/tests/compiler-tests.scm b/tests/compiler-tests.scm index 444aa508..078cb0d3 100644 --- a/tests/compiler-tests.scm +++ b/tests/compiler-tests.scm @@ -220,11 +220,22 @@ ;; Optimizer would "lift" inner-bar out of its let and replace ;; outer-bar with it, even though it wasn't visible yet. Caused by ;; broken cps-conversion (underlying problem for #1068). -(let ((outer-bar (##core#undefined))) - (let ((inner-bar (let ((tmp (lambda (x) (if x '1 (outer-bar '#t))))) - tmp))) - (set! outer-bar inner-bar) - (outer-bar #f))) +(assert (equal? 1 (let ((outer-bar (##core#undefined))) + (let ((inner-bar (let ((tmp (lambda (x) + (if x '1 (outer-bar '#t))))) + tmp))) + (set! outer-bar inner-bar) + (outer-bar #f))))) + +;; Slightly modified version which broke after fixing the above due +;; to replacement optimization getting triggered. This replacement +;; caused outer-bar to get replaced by inner-bar, even within itself, +;; thereby causing an undefined variable reference. +(assert (equal? 1 (let ((outer-bar (##core#undefined))) + (let ((inner-bar (lambda (x) + (if x '1 (outer-bar outer-bar))))) + (set! outer-bar inner-bar) + (outer-bar '#f))))) ;; Test that encode-literal/decode-literal use the proper functions ;; to decode number literals. diff --git a/tests/syntax-tests.scm b/tests/syntax-tests.scm index 89481cd7..59f7d63d 100644 --- a/tests/syntax-tests.scm +++ b/tests/syntax-tests.scm @@ -1119,6 +1119,10 @@ take tmp))) (bar #f))) +;; Deeper issue uncovered by fixing the above issue +(t 1 (letrec ((bar (lambda (x) (if x 1 (bar bar))))) + (bar #f))) + ;; Just to verify (this has always worked) (t 1 (letrec* ((foo (lambda () 1)) (bar (let ((tmp (lambda (x) (if x (foo) (bar #t)))))Trap