5.4.3.1 Local Variable Declarations
Note
This section describes the implementation of MS-VBAL §5.4.3.1 Local Variable Declarations.
Syntax
| Declaration | AST node | Symbol on the procedure's Locals |
|---|---|---|
Dim |
VariableDeclarationNode | VBLocalVariableSymbol |
Static |
VariableDeclarationNode (IsStatic) |
VBLocalVariableSymbol (IsStatic) |
A local Const is listed on Locals too, as a
VBLocalConstantSymbol; see
RD-VBAL §5.4.3.2 Local Constant Declarations.
Static Semantics
Dim and Static locals are hoisted:
VBProcedureMemberSymbol.Locals and
VBReturningMemberSymbol.Locals carry
them on the procedure symbol, as Parameters are carried.
VBProcedureMemberSymbol.LocalsandVBReturningMemberSymbol.Localslist everyDim,StaticandConstdeclared in the procedure body.- The
Localsproperty of a procedure member symbol mirrors itsParametersproperty. - ScopeTreeBuilder extracts a procedure symbol's
Localsthe same way it extracts itsParameters.
By design, a procedure's parameters and its own Dim / Static / Const locals are carried on the member
symbol, not registered as separate entries. A local therefore needs no second, flat registration of its own to
resolve by name. See
RD-VBAL §2.3.1.3 Name Resolution.
Array declarations
The declaration pass binds the declared type of an array symbol from the array-dim clause alone (MS-VBAL §5.2.3.1.3 Array Dimensions and Bounds), before any bound is evaluated:
| Array-dim clause | Example | Declared type |
|---|---|---|
| One or more bounds | Dim g(1 To 3, 0 To 4) As Long |
VBFixedSizeArrayType |
An empty () clause |
Dim b() As Long |
VBResizableArrayType |
A trailing () on the As clause |
Dim b As Long() |
VBResizableArrayType |
An empty () clause or a trailing () on the As clause, with item type Byte |
— | VBResizableByteArrayType, instead of VBResizableArrayType |
See RD-VBAL §2.4.1 Intrinsic Types. An array declaration creates an array value of the appropriate array type in the scope of the declaration; see RD-VBAL §2.5.2.1.2 Array Values.
Runtime Semantics
Hoisting of procedure-local variables is governed by
MS-VBAL §5.4.3 Data Manipulation Statements
(RD-VBAL §5.4.3 Data Manipulation Statements). Step 4 of
MS-VBAL's procedure invocation reads: "create the function result variable and any procedure extent local variables
declared within the procedure". RD-VBA implements step 4 for Dim and Static locals as well as for the function
result variable.
When a procedure is invoked (RD-VBAL §5.3.1.11 Procedure Invocation Argument Processing):
- The procedure's parameters are bound.
RuntimeProcedureInvoker.HoistLocalshoists the procedure'sDimandStaticlocals: it walks the procedure'sLocalsimmediately after parameter binding, before the body runs.- Each
Dimlocal gets a freshCallStackFrame.Push, seeded to its declared type's default value. - Each
Staticlocal that has no allocated storage gets it from ISymbolResolver.TryAllocate(see Static locals). - The procedure body runs.
| Local | Extent | Storage | On each call | When the call returns |
|---|---|---|---|---|
Dim |
Procedure extent. | A fresh CallStackFrame.Push on every call. |
Seeded to its declared type's default value on every call. A Dim local never sees a previous call's value. |
Freed with the rest of the frame. |
Static |
Module extent: it needs storage that outlives the call. | Reserved once by ISymbolResolver.TryAllocate, in the session's module-level heap. |
Sees whatever the previous call's body last wrote to it. | Kept. |
Static locals
ISymbolResolver.TryAllocate allocates a Static local's own module-extent storage: it reserves the storage in
the session's module-level heap. A Static local's storage lives in the same heap tier a module field uses; see
RD-VBAL §2.3.1.2 Session Services.
- The one-time
TryAllocatefor aStaticlocal is guarded byTryGetAddress, so only the first call allocates. - Every later call to the procedure sees whatever the previous call's body last wrote to a
Staticlocal. - By design, a
Staticlocal's storage is not reserved through ISessionSymbols.TryDefine.ISessionSymbols.TryDefine's bucket-add would register aStaticlocal's symbol a second time, since the procedure'sLocalsalready carries the name. A second registration would risk an ambiguous name, not only duplicate work.
Reading a Static local needs no dedicated mechanism. CallStackAwareSymbolResolver falls through to
session-level storage for any Local-scoped symbol that the current frame does not itself declare. A name that
refers to a symbol defined in the static locals heap resolves to a symbol that is locally scoped but preserves its
value between calls (RD-VBAL §2.3.1.3 Name Resolution).
The one-time guarded TryAllocate, with CallStackAwareSymbolResolver's fall-through for reads, is the entire
mechanism for Static locals.
A local variable also has module extent when its procedure is declared Static
(RD-VBAL §5.3.1.2 Static Procedures): it is marked Static as if the keyword were written.
Implementation
| Type or member | Role |
|---|---|
VBProcedureMemberSymbol.Locals, VBReturningMemberSymbol.Locals |
The procedure's Dim, Static and Const locals, carried on the procedure symbol (RDCore.SDK). |
ScopeTreeBuilder |
Extracts Locals for name resolution, the same way as Parameters (RDCore.SDK). |
RDCore.Runtime.Execution.RuntimeProcedureInvoker.HoistLocals |
Hoists the Dim and Static locals when the procedure is invoked (RDCore.Runtime). |
CallStackFrame.Push |
Binds a Dim local on the call's frame. |
ISymbolResolver.TryAllocate |
Allocates a Static local's module-extent storage. |
ISymbolResolver.TryGetAddress |
Guards the one-time TryAllocate. |
CallStackAwareSymbolResolver |
Falls through to session-level storage when reading a Static local. |
⏮️ RD-VBAL §5.4.3 Data Manipulation Statements | ⏭️ RD-VBAL §5.4.3.2 Local Constant Declarations