From 65808b31f5743a5c7941e6577f1af3119fb62da7 Mon Sep 17 00:00:00 2001
From: Peter Bex <peter.bex@xs4all.nl>
Date: Sun, 22 Dec 2013 22:32:39 +0100
Subject: [PATCH 2/2] Fix for #1068 (2): lambdas with local references
shouldn't replace variables.
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.
---
compiler.scm | 18 ++++++++++++++++--
tests/compiler-tests.scm | 9 +++++++++
tests/syntax-tests.scm | 4 ++++
3 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/compiler.scm b/compiler.scm
index f356eaf..b7e6899 100644
|
a
|
b
|
|
| 2159 | 2159 | (quick-put! plist 'removable #t) ) |
| 2160 | 2160 | |
| 2161 | 2161 | ;; Make 'replacable, if it has a variable as known value and if either that variable has |
| 2162 | | ;; a known value itself, or if it is not captured and referenced only once, the target and |
| | 2162 | ;; a known value[*] itself, or if it is not captured and referenced only once, the target and |
| 2163 | 2163 | ;; the source are never assigned and the source is non-global or we are in block-mode: |
| 2164 | 2164 | ;; - The target-variable is not allowed to be global. |
| 2165 | 2165 | ;; - The variable that can be substituted for the current one is marked as 'replacing. |
| 2166 | 2166 | ;; This is done to prohibit beta-contraction of the replacing variable (It wouldn't be there, if |
| 2167 | 2167 | ;; it was contracted). |
| | 2168 | ;; |
| | 2169 | ;; [*] In case the known value is a lambda, it may not refer to any local variables, |
| | 2170 | ;; to avoid scoping issues due to reordering. |
| 2168 | 2171 | (when (and value (not global)) |
| 2169 | 2172 | (when (eq? '##core#variable (node-class value)) |
| 2170 | 2173 | (let* ([name (first (node-parameters value))] |
| 2171 | 2174 | [nrefs (get db name 'references)] ) |
| 2172 | | (when (or (and (not (get db name 'unknown)) (get db name 'value)) |
| | 2175 | (when (or (and (not (get db name 'unknown)) |
| | 2176 | (let ((v (get db name 'value))) |
| | 2177 | (cond |
| | 2178 | ((not v) #f) |
| | 2179 | ((eq? '##core#lambda (node-class v)) |
| | 2180 | (let ((valparams (node-parameters v))) |
| | 2181 | (or (not (second valparams)) |
| | 2182 | (every |
| | 2183 | (lambda (v) (get db v 'global)) |
| | 2184 | (nth-value 0 (scan-free-variables value)))))) |
| | 2185 | (else #t)) |
| | 2186 | (and v (not (eq? '##core#lambda (node-class v)))))) |
| 2173 | 2187 | (and (not (get db name 'captured)) |
| 2174 | 2188 | nrefs |
| 2175 | 2189 | (= 1 (length nrefs)) |
diff --git a/tests/compiler-tests.scm b/tests/compiler-tests.scm
index 444aa50..fef0335 100644
|
a
|
b
|
|
| 226 | 226 | (set! outer-bar inner-bar) |
| 227 | 227 | (outer-bar #f))) |
| 228 | 228 | |
| | 229 | ;; Slightly modified version which broke after fixing the above due |
| | 230 | ;; to replacement optimization getting triggered. This replacement |
| | 231 | ;; caused outer-bar to get replaced by inner-bar, even within itself, |
| | 232 | ;; thereby causing an undefined variable reference. |
| | 233 | (let ((outer-bar (##core#undefined))) |
| | 234 | (let ((inner-bar (lambda (x) (if x '1 (outer-bar outer-bar))))) |
| | 235 | (set! outer-bar inner-bar) |
| | 236 | (outer-bar '#f))) |
| | 237 | |
| 229 | 238 | ;; Test that encode-literal/decode-literal use the proper functions |
| 230 | 239 | ;; to decode number literals. |
| 231 | 240 | (assert (equal? '(+inf.0 -inf.0) (list (fp/ 1.0 0.0) (fp/ -1.0 0.0)))) |
diff --git a/tests/syntax-tests.scm b/tests/syntax-tests.scm
index 89481cd..1feb665 100644
|
a
|
b
|
take
|
| 1119 | 1119 | tmp))) |
| 1120 | 1120 | (bar #f))) |
| 1121 | 1121 | |
| | 1122 | ;; Deeper issue uncovered by fixing the above issue |
| | 1123 | (letrec ((bar (lambda (x) (if x 1 (bar bar))))) |
| | 1124 | (bar #f)) |
| | 1125 | |
| 1122 | 1126 | ;; Just to verify (this has always worked) |
| 1123 | 1127 | (t 1 (letrec* ((foo (lambda () 1)) |
| 1124 | 1128 | (bar (let ((tmp (lambda (x) (if x (foo) (bar #t))))) |