Skip to content

feat: add block scoping for while/do/done#251

Merged
frendsick merged 3 commits into
mainfrom
feat/scope-stack-while
May 29, 2026
Merged

feat: add block scoping for while/do/done#251
frendsick merged 3 commits into
mainfrom
feat/scope-stack-while

Conversation

@frendsick

Copy link
Copy Markdown
Owner

Summary

  • Add scope_stack: List[List[str]] to TypeChecker and BytecodeCompiler
  • Push new frame on WhileStart, pop on WhileEnd; variables first assigned inside a while block are block-local and inaccessible after done
  • Variables already in scope before the block (pre_scope_vars) and global declarations are unaffected
  • Pattern bindings from is checks follow the same rules; for loop variables covered automatically via desugar-to-while

Test plan

  • test_scope: while scoping, for scoping, outer mutation, sibling reuse, global bypass
  • Full compiler test suite: 56 passed, 0 failed
  • Examples: 47 passed, 0 failed
  • Bootstrap: self-compilation + fixed-point verified

Closes #246

frendsick added 3 commits May 26, 2026 09:10
Add scope_stack: List[List[str]] to TypeChecker and BytecodeCompiler.
Push a new frame on WhileStart, pop it on WhileEnd.

Variables first assigned inside a while block are tracked in the
innermost scope frame. On WhileEnd those variables move to out_of_scope
and any subsequent PushVar raises UndefinedName. Variables already in
scope before the block (pre_scope_vars) or declared global are unaffected.

Pattern bindings from `is` checks follow the same scoping rules.
For-loop variables are covered automatically because for desugars to while.

Add tests: while scoping, for scoping, outer mutation, sibling reuse,
global bypass.
A variable first assigned inside a while block is marked out-of-scope on
`done`. Reassigning the same name at the function (outer) scope did not clear
that marking, so a later read was wrongly rejected as undefined — unlike
sibling-while reuse, which did clear it. The pre-scope assignment path now
clears the out-of-scope marking too.

The scope-recording logic, previously duplicated between variable assignment
and `is`-pattern bindings, is extracted into a single `record_var_scope`
method so the fix lives in one place.
The bytecode compiler pushed and popped a scope_stack on while boundaries but
never read it — scope enforcement lives entirely in the type checker.
@frendsick
frendsick merged commit 3a60916 into main May 29, 2026
3 checks passed
@frendsick
frendsick deleted the feat/scope-stack-while branch May 29, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scope stack infrastructure + while block scoping

1 participant