Fix JIT compile-let shadow binding: evaluate init before defining local

compile-let called scope-define-local eagerly as part of the let
binding, adding the new local to the scope before compile-expr ran
for the init expression. When nested lets rebound the same variable
(e.g. the hyperscript parser's 4 chained `parts` bindings), the init
expression resolved the name to the new uninitialized slot instead of
the outer one — producing nil where it should have read the previous
value.

Move scope-define-local after compile-expr so init expressions see the
outer scope's binding. Fixes all 11 JIT hyperscript parser failures.
3127/3127 JIT + non-JIT, 25/25 standalone hyperscript tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 12:03:12 +00:00
parent d715d8c4ac
commit 783ffc2ddd
3 changed files with 174 additions and 4 deletions

View File

@@ -599,11 +599,12 @@
(binding)
(let
((name (if (= (type-of (first binding)) "symbol") (symbol-name (first binding)) (first binding)))
(value (nth binding 1))
(slot (scope-define-local let-scope name)))
(value (nth binding 1)))
(compile-expr em value let-scope false)
(emit-op em 17)
(emit-byte em slot)))
(let
((slot (scope-define-local let-scope name)))
(emit-op em 17)
(emit-byte em slot))))
bindings)
(compile-begin em body let-scope tail?)))))
(define