3.5.3 Lowering Block Statements
Lowering turns a procedure body's statement tree into the InstructionList/Instruction model
(RD-VBAL §3.5.1 InstructionList,
RD-VBAL §3.5.2 Instruction).
InstructionListLowering.Lower does
it, and returns an InstructionListLoweringResult.
Lowering is pure: it uses no symbol resolver and no runtime session (RD-VBAL §3.5.5 Placement and Licensing). It emits one instruction per executable statement, plus the synthesized instructions that a block statement needs but has no source node for.
Structured blocks
A block statement (If, Select Case, a loop, With) is kept structured; it is not flattened into a
low-level jump IR. Its header(s) remain instructions of their own, addressed by ByNode like any other statement.
Only the control effects between block headers are pre-resolved offsets: an If/Case branch's
fall-through-versus-skip choice, and a loop's back-edge. Lowering computes these offsets once, instead of the
interpreter computing them on every fetch.
Lowering only flattens the statement tree. It never flattens the expression trees held in each statement's
Inputs.
Synthesized closers
Next, Loop and Wend have no AST node of their own: the whole construct is one
ForStatementNode,
DoLoopStatementNode (and so on) with a
Body (RD-VBAL §3.4.1 Block Statements). Where a loop's closer has
work to do, lowering emits a closer instruction of its own to hold that work:
| Loop | Closer instruction | Work | Node |
|---|---|---|---|
For |
ForNext |
Increments and tests the counter. | null (synthesized) |
For Each |
ForEachNext |
Advances to the next element. | null (synthesized) |
While…Wend, Do While, Do Until |
Jump back to the header |
— | null (synthesized) |
Do…Loop While, Do…Loop Until |
LoopBack |
Evaluates the condition and decides whether to branch back. | The loop's own node: the closer is the loop's only instruction. |
Do…Loop |
Jump back to the body |
— | The loop's own node: the closer is the loop's only instruction. |
A synthesized instruction has Node = null, so it is never a value in ByNode.
A synthesized Next closer does not reuse the loop's own node. Doing so would take the loop's ByNode entry away
from the opener, which is the more useful attribution for a breakpoint on the For/For Each line.
If and Select Case need no synthesized closer. Falling out of the last branch, or out of the Else/Case Else
that needs no condition, already lands where the construct's own End/Else chaining places it, with
nothing left to do.
Branch trailing jumps
After an If/ElseIf/Case branch's body, lowering always emits a synthesized, unconditional Jump to right
past the whole construct. It does so even for the last branch, where the jump is redundant with the
fall-through.
Always emitting the trailing Jump keeps the emission logic uniform, instead of special-casing "is this the last
branch", at the cost of one extra instruction that does not change behaviour.
Loop exits
Exit For and Exit Do resolve against the innermost enclosing loop of the matching kind. Lowering sets the
ExitLoop instruction's Target to the offset right past that loop's closer, so the interpreter needs no
runtime search.
| Statement | Needs an enclosing |
|---|---|
Exit For |
For or For Each loop (RD-VBAL §5.4.2.5 Exit For Statement). |
Exit Do |
Loop of one of the five Do…Loop forms (RD-VBAL §5.4.2.7 Exit Do Statement). |
A While…Wend loop satisfies neither Exit For nor Exit Do: MS-VBAL gives While…Wend no exit statement of
its own (RD-VBAL §5.4.2.2 While Statement). An Exit Do written
inside a While…Wend is not consumed by it; it resolves against the Do loop, if any, that encloses the
While…Wend.
An Exit For or Exit Do that has no enclosing loop of the matching kind, including an Exit Do inside a While…Wend
that no Do loop encloses, is VBC09313 or VBC09312, and lowers to no
instruction. An Exit Sub, Exit Function or Exit Property in the wrong kind of procedure is
VBC09332, VBC09314 or VBC09315, and lowers to no instruction
either. The rule is the one StatementStaticSemanticsEvaluator asks
(ExitStatementStaticSemantics); the kind of procedure is a parameter
of Lower, and is not checked when it is not given.
EnclosingWith is static
Every instruction lexically inside a With block, however deeply nested (through an If or a loop), carries
EnclosingWith set to that With's opener offset. When lowering leaves the block, EnclosingWith is restored to
its value before the block.
EnclosingWith is computed once, at lowering time. It is a purely lexical fact about the instruction, not a
runtime stack the interpreter pushes and pops. Because it is static, a GoTo into or out of a With block
leaves no stale state to unwind (RD-VBAL §5.4.2.21 With Statement).
Dead conditional-compilation branches
A dead #If/#ElseIf/#Else branch is never lowered. Lower takes the source ranges that
RDCore.Runtime.Semantics.Precompiler.PrecompilerLiveBranchEvaluator found not live, as
InstructionLoweringOptions.DeadRanges.
A statement or label lexically inside a dead range, at any depth, is skipped entirely: it gets no instruction,
no ByNode entry, and no label definition. The result is the same as if the excluded source were absent, in the
same way that the MS-VBA preprocessor logically removes it before the rest of the language sees it
(MS-VBAL §3.4.2 Conditional Compilation If Directives).
Labels and diagnostics
Lowering builds the label table that a jump statement's target resolves against, and needs every label to
resolve to a single, unambiguous offset. Lowering alone resolves a jump's target label, for
GoSub as for GoTo: a GoSub statement's Target, and an On…GoSub statement's Targets, hold the
resolved offsets.
| Condition | Lowering result | Diagnostic |
|---|---|---|
A label operand (GoTo, GoSub, On…GoTo, On…GoSub, On Error GoTo, Resume) names a line label or line number the procedure does not define. |
The operand carries a null target. |
VBC09309 — Label not defined |
| A label is defined more than once. | The first offset the label was defined at is kept; every jump to the label resolves against that first definition. | VBC09319 — Duplicate label definition |
An Exit For/Exit Do has no enclosing loop of the matching kind. |
No instruction is lowered for it. | VBC09313 — Exit For not within For...Next, VBC09312 — Exit Do not within Do...Loop |
An Exit Sub/Exit Function/Exit Property is in the wrong kind of procedure. |
No instruction is lowered for it. | VBC09332, VBC09314, VBC09315 |
Lowering never fails outright: it always produces a complete InstructionList, whether or not every label
resolved and every statement was where it may be.
Whether to refuse to run a body that lowered with errors is a decision for the component that executes the body, not for lowering (RD-VBAL §3.5.4 Execution).
Other statements
Any statement kind that lowering does not give a dedicated shape lowers as Simple
(RD-VBAL §3.5.2 Instruction).