5.2.3 Module Declarations
Note
This section describes the implementation of MS-VBAL §5.2.3 Module Declarations.
A standard module's non-Private members (an explicit Public / Global / Friend, or an implicit
procedure-like member) are also declared in the project scope. Because of this, a sibling module resolves them
without qualification (RD-VBAL §2.3.1.3 Name Resolution).
5.2.3.1 Module Variable Declaration Lists
Note
This section describes the implementation of MS-VBAL §5.2.3.1 Module Variable Declaration Lists.
👉 When an array declaration specifies no item type, the declared item type of the array is Variant
(RD-VBAL §2.4.1 Intrinsic Types;
RD-VBAL §2.5.2.1.2 Array Values).
5.2.3.1.1 Variable Declarations
Note
This section describes the implementation of MS-VBAL §5.2.3.1.1 Variable Declarations.
Any variable declared with an As New clause is an automatic instantiation variable
(MS-VBAL §2.5.1 Automatic Object Instantiation).
Its symbol carries the AutoInstantiated property of
SymbolProperties
(RD-VBAL §2.5.1 Runtime Entities;
RD-VBAL §3.1.1 Attributes).
The default instance variable of a predeclared class is created as if declared As New
(RD-VBAL §5.2.4 Class Module Declarations, §5.2.4.1.2).
5.2.3.1.2 WithEvents Variable Declarations
This section corresponds to MS-VBAL §5.2.3.1.2 WithEvents Variable Declarations.
A WithEvents variable is a variable of a class module whose declared type is a class with events. The symbol of such a variable has the WithEvents property (SymbolProperties.WithEvents), which the language server sets from the WithEvents keyword and sends to the environment host in its descriptor (SymbolDescriptor.IsWithEvents). The procedures of the module named VariableName_EventName handle that event of the object the variable holds (RD-VBAL §5.3.1.8).
Assigning the variable with Set attaches its handlers to the object it is given, and detaches them from the object it held (RD-VBAL §5.4.3.9).
The declared type must be a specific class with at least one event, and must not be the class of the module containing the declaration; ClassModuleEventSemantics reports one that is not as VBC09324. A type that is not resolved is not reported.
5.2.3.1.3 Array Dimensions and Bounds
Note
This section describes the implementation of MS-VBAL §5.2.3.1.3 Array Dimensions and Bounds.
The declaration pass binds the declared type of an array symbol from the array-dim clause alone, before any bound is evaluated (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 (RD-VBAL §2.5.2.1.2 Array Values):
| Array-dim clause | Example | Declared type | Array value created |
|---|---|---|---|
| One or more bounds (dimension specifications) | Dim g(1 To 3, 0 To 4) As Long |
VBFixedSizeArrayType | VBFixedSizeArrayValue |
An empty () clause |
Dim b() As Long |
VBResizableArrayType | VBResizableArrayValue |
An empty () clause, with a Byte item type |
Dim b() As Byte |
VBResizableByteArrayType | VBResizableByteArrayValue |
An array has up to 60 dimensions.
An array is declared by the () after its name, and only so: a trailing () on the As clause (As Long(), the array-designator of
MS-VBAL §5.3.1.4) is the type of what a Function, a Property Get or a Declare's Function returns, and is a syntax error anywhere else, as it
is in MS-VBA (recorded in Excel): a variable of a module, a local (Dim, Static), a field of a Type, a constant, a parameter (of a procedure, an
event or a Declare), or the As clause of a ReDim. DeclarationStaticSemanticsEvaluator.CheckSyntax reports it.
| Declaration | Outcome |
|---|---|
Function F() As Long(), Property Get P() As Long(), Declare Function F Lib "…" () As Long() |
Valid: what is returned is an array of Long. |
Dim b As Long(), Public b As Long(), Static s As Long(), F As Long() in a Type |
VBC00001 Syntax error. Write Dim b() As Long. |
Sub X(a As Long()), Event E(a As Long()) |
VBC00001 Syntax error. Write Sub X(a() As Long). |
ReDim a(2) As Long(), Const K As Long() = 1 |
VBC00001 Syntax error. |
Resolving an omitted lower bound against Option Base is specified in
RD-VBAL §5.2.1 Option Directives (§5.2.1.2).
A bound is a constant expression Let-coerced to Long, reduced when the module is compiled, of a variable, a local and a
field of a Type alike, as MS-VBA reduces it (recorded in Excel):
| Bound | Example | Outcome |
|---|---|---|
| No constant expression | Dim a(n) As Long, X(Count) As Long in a Type |
VBC09350 Constant expression required |
Past the range of a Long, written or given by a constant |
Dim a(1E10) As Long, Const K = 1E10: Dim a(K) As Byte |
VBC09352 Overflow |
| An upper bound below the lower one | Dim a(5 To 1) As Long, Dim a(-1) As Long |
VBC09365 Range has no values |
A fraction, or a String that reads as a number |
Dim a(2.6) As Long, Dim a("3") As Long |
Valid: the bound is rounded half to even, or read, as Let-coercion to Long does: 3. |
Len or LenB of a constant expression |
Const K = "abcd": Dim a(Len(K)) As Long |
Valid: 4, and 8 for LenB, of a local and of a module's variable alike. MS-VBAL §5.6.16.1 lists both among constant expressions; MS-VBA admits them in a bound (recorded in Excel), though not in the value of a Const: see below. |
MS-VBAL §5.6.16.1 lists Len and LenB among constant expressions, and so a valid value of a Const. MS-VBA refuses them in a Const of a module, and
LenB in one of a procedure, with VBC09350 Constant expression required (recorded in Excel), and so does RD-VBA: that is no
check MS-VBA skips but one it adds, so no warning goes with it.
🎯 A Const of a procedure valued Len of a constant (Const K3 = Len(K)) compiles in MS-VBA, inconsistently, and running it crashed the host. RD-VBA
compiles it, gives it the length MS-VBAL gives it (4), and warns with RDC11021. String * Len(K) is a syntax error in both.
5.2.3.1.4 Variable Type Declarations
Note
This section describes the implementation of MS-VBAL §5.2.3.1.4 Variable Type Declarations.
A declared type name binds in the type binding context. With the
CompositeSymbolResolver, a module's As SomeType
binds to a sibling module's Type or Enum, or to a class, not only to a reserved data-type name
(RD-VBAL §2.3.1.3 Name Resolution). A name that binds an Enum type, of the
workspace or of a library, declares a Long (RD-VBAL §5.2.3.4 Enum Declarations). A
name that binds a procedural module or a project is not a type, and the declaration is the error
RD-VBAL §5.6.16.7 Type Expressions lists for it. A type of the project ranks
ahead of a library's procedural module of the same name (RD-VBAL §2.3.1.3 Name Resolution).
The length of a fixed-length String * n is "the data value of its <INTEGER> element or the data value referenced by
its <constant-name> Let-coerced to declared type Long", reduced when the module is compiled, wherever it is written:
a variable, a local, an element of an array, a field of a Type, the As clause of a ReDim. MS-VBA refuses
what is no length (recorded in Excel):
| Length | Example | Outcome |
|---|---|---|
1 to 65526, written or given by a constant or an Enum member |
String * 10, String * &H10, String * Red |
Valid. |
A constant whose value is a fraction or a numeric String |
Const K = 2.5, Const K = "3" |
Valid: 2 and 3, as Let-coercion to Long has them. |
| Less than 1, or more than 65526 | String * 0, String * 65527, Const K = 70000: … String * K |
VBC09375 Invalid length for fixed-length string |
| A number written past 65535, or no integer | String * 65536, String * 1.5 |
VBC00001 Syntax error |
| A name that is no constant | String * n, a variable |
VBC09350 Constant expression required |
A name nothing declares, under Option Explicit |
String * NoSuch |
VBC09302 Variable not defined |
Note
Not implemented. A constant whose value is an expression (Const K = 1 + 2) is reduced to report a length that
is invalid, but the variable String * K declares is a variable-length String: its type is decided before the
constants are folded.
MS-VBAL §5.2.3.1.4: the type specified in an As New clause must be a named class, and must be creatable unless
it is declared in the same project. DeclarationStaticSemanticsEvaluator checks every declaration that has an
As New clause - a variable, a field, an array (its element type), a field of a Type - as MS-VBA does
(recorded in Excel):
As New of |
Outcome |
|---|---|
A class of the project, creatable or not (VB_Creatable = False) |
Valid. |
A creatable class of a library (As New Collection) |
Valid. |
A class of a library that cannot be created (As New ErrObject, As New Workbook) |
Compile error VBC09368 Invalid use of New keyword. |
A user-defined type, or an Enum (of the project or of a library) |
VBC09368. |
A WithEvents variable (Private WithEvents w As New Widget, MS-VBAL §5.2.3.1.2) |
VBC09368. |
An intrinsic type (As New Long, As New String, As New Variant, As New Object) |
Compile error VBC00001 Syntax error: New is followed by a type name no class can have. |
Important
MS-VBA compiles As New of a library's class that cannot be created, and raises run-time error -2147221164
Class not registered when the variable is first referred to. RD-VBA reports it when the module is compiled, as
MS-VBAL §5.2.3.1.4 has it; the verbose message says what MS-VBA does.
An automatic instantiation variable that is dependent on another (MS-VBAL §2.5.1) is created on its first reference too:
| Variable | Example | First reference |
|---|---|---|
An element of an array declared As New |
Dim a(1 To 3) As New Collection, then a(2).Add 1 |
Creates the object of that element, and only that one. |
A field of a Type declared As New |
Dim r As TRecord, then r.Items.Add 1 |
Creates the object of that field. A copy of the record (r2 = r) holds the same object. |
A field of a class module declared As New |
Public Items As New Collection, then obj.Items.Count |
Creates the object of that field, of that object. |
Each is never Nothing when it is referred to: after Set a(2) = Nothing, the next reference to a(2) creates another
object.
5.2.3.1.5 Implicit Type Determination
This section corresponds to MS-VBAL §5.2.3.1.5 Implicit Type Determination.
Note
Reserved. This section has no content yet.
5.2.3.2 Const Declarations
This section corresponds to MS-VBAL §5.2.3.2 Const Declarations.
Note
Reserved. This section has no content yet.
5.2.3.3 User Defined Type Declarations
Note
This section describes the implementation of MS-VBAL §5.2.3.3 User Defined Type Declarations.
MS-VBAL §5.2.3.3 uses the same accessibility wording as MS-VBAL §5.2.3.4 Enum Declarations: a user-defined type is accessible within the enclosing project, or within the enclosing module.
A public user-defined type declared in a class module reaches the project scope. A class module's other members need an instance to be reached through, but a declared type needs none (RD-VBAL §5.2.3.4 Enum Declarations).
The data type of a UDT value is defined by the UDT declaration of its declared type (RD-VBAL §2.5.2.1.3 User-Defined Type (UDT) Values).
5.2.3.4 Enum Declarations
Note
This section describes the implementation of MS-VBAL §5.2.3.4 Enum Declarations.
MS-VBAL §5.2.3.4 states that "the Enum type and its Enum members are accessible within the enclosing project" / "within the enclosing module".
An Enum's members are lexically scoped like the Enum type itself: accessible within the enclosing project, or
within the enclosing module.
| Aspect | Enum member |
|---|---|
| Access modifier | An Enum member has no access modifier of its own. |
| Visibility | An Enum member takes its visibility from its Enum's own access modifier. |
| Symbol parent | An enum constant (Enum member) symbol parents to its Enum rather than to a scope. |
| Scope placement | ScopeTreeBuilder resolves an Enum member's scope placement through its Enum (RD-VBAL §2.3.1.3 Name Resolution). |
👉 Because Enum members are placed through their Enum, an Enum member such as vbSunday is a name on its own,
resolvable without qualification.
A public Enum declared in a class module reaches the project scope. A class module's other members need an
instance to be reached through, but a declared type (an Enum or a user-defined type) needs none.
MS-VBAL §5.2.3.4: "When an enum name (possibly qualified by a project) appears in an <as-type> clause of any
declaration, the meaning is the same as if the enum name was replaced with the declared type Long." A variable, a
field, a parameter or a function's result declared as an Enum - of the enclosing module, of another module
(As Mod2.Colour), of a class module, or of a library - holds a Long: 0 until it is assigned, and a value assigned to
it is let-coerced to Long (c = 1.7 stores 2). The type is VBEnumType, whose value is a Long. An Enum symbol
carries its type, with its members, whether a library or the workspace declares it: the environment host reads an
Enum of the workspace with its type (SymbolDescriptorReader). A member's value is read from its own symbol, not from
the type. A declaration that names an Enum of the workspace has the type only once the host has defined the module
that declares the Enum (below).
Since an Enum's name means the declared type Long, an Enum and Long, or two Enums, of the workspace or of a
library, are the same declared type where these match two declared types (VBEnumType.AsDeclaredType: Long for
an Enum, any other type itself):
| Where | What is matched |
|---|---|
| An event handler (RD-VBAL §5.3.1.8) | Each parameter and the event's. |
| An implemented name declaration (RD-VBAL §5.3.1.9) | Each parameter, the result, or what a property carries, and the interface member's. |
A ByRef argument of a call (RD-VBAL §5.3.1.11) or of a RaiseEvent (RD-VBAL §5.4.2.20) |
The parameter and the variable it is given, which it is bound to rather than given a copy of. |
An Integer and a Long are not the same declared type, and only the type itself is mapped, not an array's element
type (below). A record stores a field of an Enum as the Long it holds, four bytes: in memory, which LSet
between records copies (RD-VBAL §5.4.3.6), and in a file, which Put
writes and Get reads (RD-VBAL §5.4.5.11).
Note
Not implemented. A member of an Enum the workspace declares has no value yet, so reading one stops the run
(RD-VBAL §6.1.1 Predefined Enums); a variable of the Enum holds what is
assigned to it.
A member of a variable of an Enum (c.cRed) is not refused when the module loads, though the variable is a Long,
which has no members: it stops the run, as a member of a Long variable does.
The host defines the modules of the workspace one at a time, so a declaration that names an Enum of the workspace,
bare or qualified, in a module the host defines before the module that declares the Enum keeps no type: assigning
to it raises error 13, Type mismatch, and reading it raises error 51, Internal error, where a variable of a
user-defined type of such a module stops the run.
An array's element type is not mapped: an array of an Enum given to a ByRef array parameter of Long, or an
array of Long given to one of an Enum, is not bound to the parameter, and copying it to the parameter raises
error 13, Type mismatch.
5.2.3.5 External Procedure Declaration
This section corresponds to MS-VBAL §5.2.3.5 External Procedure Declaration.
Note
Reserved. This section has no content yet.
5.2.3.6 Circular Module Dependencies
This section corresponds to MS-VBAL §5.2.3.6 Circular Module Dependencies.
Note
Reserved. This section has no content yet.
⏮️ RD-VBAL §5.2.2 Implicit Definition Directives | ⏭️ RD-VBAL §5.2.4 Class Module Declarations