Table of Contents

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.Locals and VBReturningMemberSymbol.Locals list every Dim, Static and Const declared in the procedure body.
  • The Locals property of a procedure member symbol mirrors its Parameters property.
  • ScopeTreeBuilder extracts a procedure symbol's Locals the same way it extracts its Parameters.

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):

  1. The procedure's parameters are bound.
  2. RuntimeProcedureInvoker.HoistLocals hoists the procedure's Dim and Static locals: it walks the procedure's Locals immediately after parameter binding, before the body runs.
  3. Each Dim local gets a fresh CallStackFrame.Push, seeded to its declared type's default value.
  4. Each Static local that has no allocated storage gets it from ISymbolResolver.TryAllocate (see Static locals).
  5. 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 TryAllocate for a Static local is guarded by TryGetAddress, so only the first call allocates.
  • Every later call to the procedure sees whatever the previous call's body last wrote to a Static local.
  • By design, a Static local's storage is not reserved through ISessionSymbols.TryDefine. ISessionSymbols.TryDefine's bucket-add would register a Static local's symbol a second time, since the procedure's Locals already 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