v4 Semantics: loop rules and scope
Work through examples, try the reasoning yourself, then put it into practice.
View SlidesBefore you start
- Trace source-order scopes and v4 for AST fields.
What you will practice
- Track loop legality within the current function only.
- Separate initializer scope from body block scope.
- Analyze both operands and unreachable-looking statements before execution.
In this lesson
Lesson
3 min readv4 Semantics: loop rules and scope
The v4 analyzer adds loop context alongside function context. A break or continue needs an enclosing loop in the same function. A nested function inside a loop cannot jump into its caller's loop. Save/reset loop depth while checking a function body, then restore the outer function's loop context.
Worked reconstruction
while (true) {
fn inner() { break; }
continue;
}
| Traversal point | Function context | Loop depth | Decision |
|---|---|---|---|
| outer while body | top-level | 1 | loop control allowed |
| enter inner function | inner | 0 | reset loop context |
| inner break | inner | 0 | break outside loop |
| leave inner | top-level | 1 | restore outer context |
| outer continue | top-level | 1 | valid |
Merely counting lexical loop nodes without respecting function boundaries would accept the invalid break. A loop declared inside inner would give inner its own valid target.
Scope and visit order
Open a scope for the for initializer, analyze init, condition, and post, then analyze the body as a nested block while loop depth is positive. Close both scopes when their constructs end. Header names are visible in the body and post; body-only names are unavailable in post. A body declaration may shadow an initializer name without redeclaring it in the same scope.
The analyzer's deterministic visit order (post before body) is not execution
order. Runtime runs body before post. Static traversal checks every expression;
it does not run the program to decide which names deserve validation.
For false && missing, the missing name remains an error. A constant-looking
infinite loop does not permit undefined names in its post clause.
Preserved boundaries
Const, declared-before-use, return placement, and known-function arity remain active. Type checking is still dynamic: v4 permits truthy numeric conditions. The compiler assumes these structural and placement checks already passed; letting an illegal cross-function break through can otherwise turn it into a missing or incorrectly targeted jump.
Debugging lab
An analyzer uses one loopDepth counter across nested function calls during traversal. It accepts the worked example, then rejects the outer continue after setting depth to zero. What two operations must be paired?
Compare your diagnosis after attempting the lab
Save the enclosing depth, reset to zero for the function, and restore it afterward. Reset without restoration breaks valid enclosing code; no reset incorrectly allows cross-function control transfer.
Transfer task
Construct a for loop whose body shadows its initializer variable and a post clause that uses a body-only variable. Predict acceptance of the first and the undefined-name diagnostic for the second without executing either program.
Carry forward
Carry a placement-valid AST into lowering. Preserve tests for function boundaries, scope shadowing, and static traversal of skipped runtime branches.
Build semantic-analysis-v4. Keep a before/after trace of one failing test. Use the checkpoint to diagnose specific skills; passing it does not replace implementing and testing the artifact.
Practice sequence
Use the order below for a guided path, or choose an item to revisit. A pass records a successful attempt; try a fresh example later to check what you can reconstruct.