Split env-bind! from env-set!: fix lexical scoping and closures
Two fundamental environment bugs fixed: 1. env-set! was used for both binding creation (let, define, params) and mutation (set!). Binding creation must NOT walk the scope chain — it should set on the immediate env. Only set! should walk. Fix: introduce env-bind! for all binding creation. env-set! now exclusively means "mutate existing binding, walk scope chain". Changed across spec (eval.sx, cek.sx, render.sx) and all web adapters (dom, html, sx, async, boot, orchestration, forms). 2. makeLambda/makeComponent/makeMacro/makeIsland used merge(env) to flatten the closure into a plain object, destroying the prototype chain. This meant set! inside closures couldn't reach the original binding — it modified a snapshot copy instead. Fix: store env directly as closure (no merge). The prototype chain is preserved, so set! walks up to the original scope. Tests: 499/516 passing (96.7%), up from 485/516. Fixed: define self-reference, let scope isolation, set! through closures, counter-via-closure pattern, recursive functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
web/forms.sx
10
web/forms.sx
@@ -98,7 +98,7 @@
|
||||
(params (get parsed "params"))
|
||||
(body (get parsed "body")))
|
||||
(let ((hdef (make-handler-def name params body env opts)))
|
||||
(env-set! env (str "handler:" name) hdef)
|
||||
(env-bind! env (str "handler:" name) hdef)
|
||||
hdef))))
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
(doc (if has-doc (nth args 2) ""))
|
||||
(body (if has-doc (nth args 3) (nth args 2))))
|
||||
(let ((qdef (make-query-def name params doc body env)))
|
||||
(env-set! env (str "query:" name) qdef)
|
||||
(env-bind! env (str "query:" name) qdef)
|
||||
qdef))))
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
(doc (if has-doc (nth args 2) ""))
|
||||
(body (if has-doc (nth args 3) (nth args 2))))
|
||||
(let ((adef (make-action-def name params doc body env)))
|
||||
(env-set! env (str "action:" name) adef)
|
||||
(env-bind! env (str "action:" name) adef)
|
||||
adef))))
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
(nth args (+ idx 1))))))
|
||||
(range 1 max-i 2)))
|
||||
(let ((pdef (make-page-def name slots env)))
|
||||
(env-set! env (str "page:" name) pdef)
|
||||
(env-bind! env (str "page:" name) pdef)
|
||||
pdef))))
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
(bindings (stream-chunk-bindings chunk)))
|
||||
(for-each
|
||||
(fn ((key :as string))
|
||||
(env-set! env (normalize-binding-key key)
|
||||
(env-bind! env (normalize-binding-key key)
|
||||
(get bindings key)))
|
||||
(keys bindings))
|
||||
env)))
|
||||
|
||||
Reference in New Issue
Block a user