5.4.2.3 For Statement
Note
This section describes the implementation of MS-VBAL §5.4.2.3 For Statement.
Syntax
| AST node | Instruction kind(s) | Notes |
|---|---|---|
| ForStatementNode | ForOpener |
The For opener. Its End is the offset right past the Next closer. |
| — (synthesized) | ForNext |
The Next closer; Node is null. Its Target is the body's first instruction; its Matching is the offset of its ForOpener. |
Next has no AST node of its own: the whole For...Next construct is one ForStatementNode with a Body
(RD-VBAL §3.4.1 Block Statements). The instruction fields are described in
RD-VBAL §3.5.2 Instruction.
Static Semantics
Reassigning a For loop counter inside the loop body is legal VBA.
Performance-related diagnostics should be issued when a
VBCollectionType is accessed by index within the body
of a For...Next loop (RD-VBAL §2.4.2 Non-intrinsic Types).
Runtime Semantics
A For loop needs per-activation state. Its opener stores a
ForLoopState on the activation, and its closer reads it back.
ForOpener
- Evaluate
start-value,end-valueandstep-incrementonce each, in that order. A missingstep-clausedefaults to the integer value1; that default is never itself evaluated as a source expression. - Let-coerce all three to the type the loop counts in (see below): an object to its default member's value, a
Stringto the number it reads as. A value that is no number raises what Let-coercing it raises: 13, or 94 forNull. - Let-assign the counter to
start-value, through the same Let-assignment machinery aLetstatement uses (RD-VBAL §5.4.3.8 Let Statement). - Store
endandstep, together with the counter symbol and the counter's own expression node, as aForLoopState, for every later step to reuse. - Check steps 1 and 2 of the MS-VBAL §5.4.2.3 algorithm immediately: "if step is zero or positive and the counter already exceeds end", and "if step is negative and the counter already falls short".
- When the counter is already out of range, branch to
Instruction
.End, right past the loop. Otherwise, fall through into the body.
ForNext
- Read the stored
ForLoopStateback viaInstruction.Matching. - Read the counter's current value. The body may have reassigned the counter directly.
- Add
stepto it through the addition operator (MS-VBAL §5.6.9.3 Arithmetic Operators; RD-VBAL §5.6.9.3 Arithmetic Operators). The addition is an overflow-checked operation, not an unchecked CLR addition. - Let-assign the sum back to the counter.
- Re-test the counter in the same way as
ForOpener: branch back to the body (Instruction.Target) while the counter is still in range, and fall through past the loop otherwise.
The type a loop counts in
MS-VBAL §5.4.2.3 checks that the start, the end and the step are "Let-coercible to Double", then proceeds "using
the original, uncoerced values". Compared as they are, the counter of For i = 1 To "40" would never exceed "40": a
number is less than any String (MS-VBAL §5.6.9.5), and the loop would not end. The values are checked so that
they can be counted with: RD-VBA counts with them as numbers, as MS-VBA does, which counts that loop 40 times (recorded
in Excel, as is the rest of this section).
A counter of a declared type counts in that type, and its start, end and step are Let-coerced to it:
For k = 1 To 2.6 counts to 3 with an Integer k, and Step 0.5 is a step of 0.
A Variant counter counts in the widest type of its start, end and step:
| Rank | Type | Of |
|---|---|---|
| 1 | Byte |
Byte; and of a loop whose start, end and step are all Empty |
| 2 | Integer |
Integer, Boolean |
| 3 | Long |
Long |
| 4 | LongLong |
LongLong |
| 5 | Double |
Single, Double, String |
| 6 | Currency |
Currency |
| 7 | Date or Decimal |
Date, Decimal: of a tie between the two, the end's type wins over the start's, and the step's over neither |
Empty has no rank, and nor has the step 1 of a loop that writes none: For i = CByte(1) To CByte(3) counts in
Byte. A loop that counts in Date reads a String as a date only, not as the number a Let-coercion to Date would
also read it as: "1/1/1900" is a start, "1" is error 13.
Range Test
The range checks of ForOpener and ForNext use the relational operators > and <
(MS-VBAL §5.6.9.5 Relational Operators;
RD-VBAL §5.6.9.5 Relational Operators), not a raw numeric
comparison. The counter's declared type (Currency, Decimal, Date as Double, …) has comparison semantics
specified by MS-VBAL, which a CLR > or < comparison does not implement.
The counter, the end and the step are of the one type the loop counts in, so the comparison is between numbers of a
type. Only the sign of step is read directly from its numeric value, once, when the loop starts
(ForLoopState.CountsDown). The MS-VBAL §5.4.2.3 algorithm treats the step's sign as a classification, not as a
VBA-visible comparison expression.
| Step | Out of range when |
|---|---|
| Zero or positive | The counter exceeds end (counter > end). |
| Negative | The counter falls short of end (counter < end). |
Errors
A label is scoped to the whole procedure, so a GoTo can land directly on the Next closer
(RD-VBAL §5.4.1.1 Statement Labels). When ForNext finds no stored
state for its Matching offset, its ForOpener never ran in this activation.
| Condition | Run-time error |
|---|---|
ForNext finds no stored ForLoopState for its Matching offset (a GoTo landed directly on the closer). |
92 — For loop not initialized |
The addition of step to the counter overflows. |
6 — Overflow |
The start, the end or the step is no number: "abc", an Error, an array, a String that is no date in a loop that counts in Date. |
13 — Type mismatch |
The start, the end or the step is Null. |
94 — Invalid use of Null |
The start, the end or the step is out of the range of the type the loop counts in (For b = 1 To 300 with a Byte b). |
6 — Overflow |
Run-time error 92 is VBRuntimeErrorId.ForLoopNotInitialized
(RD-VBAL §2.6.3 Runtime Errors).
Implementation
ProcedureExecutordispatchesForOpenerandForNext.- The
Nextcloser increments and tests the counter, so lowering synthesizes an instruction (Node = null) to hold that work (RD-VBAL §3.5.3 Lowering Block Statements). ForOpenerstoresend,step, the counter symbol and the counter's own expression node as aRDCore.SDK.Runtime.Shared.ForLoopState. The state is read back via ICallStackFrame.TryGetForLoopState, and written viaCallStackFrame.SetForLoopState.- A
Forloop's state holds more than a single value: counter symbol, counter expression, end and step. It therefore has its own SDK type,ForLoopState, rather than the single block-state value thatWithandSelect Caseuse (RD-VBAL §3.5.4 Execution). ForNextuses the sameInstruction.Matchingfield that aCaseheader uses for its back-reference to itsSelect(RD-VBAL §5.4.2.10 Select Case Statement).- The range test uses
RDCore.Runtime.Semantics.Operators.Relational.BinaryGtRelationalOperatorRuntimeSemanticsandBinaryLtRelationalOperatorRuntimeSemantics. The increment usesRDCore.Runtime.Semantics.Operators.Arithmetic.BinaryAdditionOperatorRuntimeSemantics. - Every location-bearing node passed to the loop's operator calls is a node the loop already has, never a synthesized
one: the counter's own expression or, for the first assignment, the loop's start expression.
The same rule applies to
Caseclause matching, which relies on the operator node parameter being typed ExpressionNode (RD-VBAL §5.4.2.10 Select Case Statement). - The verbose message of
ForLoopNotInitialized, when aNextcloser runs without its opener, is the resx entryVBForLoopNotInitialized_Verbose, provided in both languages.
⏮️ RD-VBAL §5.4.2.2 While Statement | ⏭️ RD-VBAL §5.4.2.4 For Each Statement