Table of Contents

5.2.4 Class Module Declarations

Note

This section describes the implementation of MS-VBAL §5.2.4 Class Module Declarations.

Class modules defined in workspace source code have no means to inherit another class module in the Object-Oriented Programming sense of inheritance (RD-VBAL §2.4.2 Non-intrinsic Types).

A public Enum and a public user-defined type declared in a class module reach 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 Module Declarations, §5.2.3.3 and §5.2.3.4).

5.2.4.1 Non-Syntactic Class Characteristics

Note

This section describes the implementation of MS-VBAL §5.2.4.1 Non-Syntactic Class Characteristics.

VBClassModuleSymbol.AutomationKind (VBAutomationKind) distinguishes an Automation-capable class module from an IUnknown-only class module:

AutomationKind Class module
Dispatch Automation-capable (VT_DISPATCH). This is the default.
Unknown IUnknown-only.

Every RD-VBA class module has AutomationKind Dispatch (VT_DISPATCH). See RD-VBAL §6.1.1 Predefined Enums (§6.1.1.16 VbVarType).

5.2.4.1.1 Class Accessibility and Instancing

Note

This section describes the implementation of MS-VBAL §5.2.4.1.1 Class Accessibility and Instancing.

The accessibility and instancing of a class module are determined by two module attributes (RD-VBAL §3.1.1 Attributes, §3.1.1.2 and §3.1.1.3):

Attribute Determines Value
VB_Exposed Whether a class module is visible at all to a referencing project. False for private modules, True for public modules.
VB_Creatable Whether a class module can be directly instantiated using a New (or CreateObject) expression from a referencing project. Must be False in a VBA module. May be True in a VB6 module, for RD-VBA clients that support the VB6 language.

A public module may be consumed by a referencing project. Whether a new instance of a public module can be created outside the enclosing project that defines it depends on the value of its VB_Creatable attribute.

A not-creatable class module can only be directly instantiated within the project it is defined in (the enclosing project). An instance of a not-creatable class may be consumed by any referencing project if the class module is exposed.

👉 Together, VB_Creatable and VB_Exposed determine the instancing mode of a class module:

Instancing mode VB_Exposed VB_Creatable
Private False False
PublicNotCreatable True False
PublicCreatable True True

The PublicCreatable instancing mode is not a legal VBA configuration. RD-VBA implementations may allow it, with semantic flags issued, if the host environment is configured to allow building library projects.

5.2.4.1.2 Default Instance Variables Static Semantics

Note

This section describes the implementation of MS-VBAL §5.2.4.1.2 Default Instance Variables Static Semantics.

The VB_PredeclaredId attribute (RD-VBAL §3.1.1 Attributes, §3.1.1.6) determines whether the environment host declares a global auto-object instance of the class with a predeclared ID. The identifier name of that global auto-object is the same as the name of the class module it is a predeclared instance of.

A default instance is what object-oriented VBA code is built on: a factory method called off a stateless default instance (Set w = Widget.Create(42)) returns a new object, often through an interface that only exposes getters, while the setters it uses to initialize the object are Friend members that only the factory calls.

Static Semantics

👉 Deferred class types cannot be presumed to have a default instance (RD-VBAL §2.4.4 Deferred Types).

Important

MS-VBAL §5.2.4.1.2 says: "It is invalid for this named variable to be the target of a Set assignment". MS-VBA compiles a Set to a default instance and runs it, and so does RD-VBA: the default instance variable is a valid Set target, whatever is assigned to it (see RD-VBAL §5.4.3.9 Set Statement). Two default instances are not, in MS-VBA as in RD-VBA:

  • The default instance of an extensible class, a document module such as ThisWorkbook or Sheet1 (RD-VBAL §3.1.1.8 VB_Extensible). The host gives each of its documents a property of that name, and a Set of it, whatever is assigned, is compile error VBC09345 Invalid use of property.
  • The host's Debug object. Set Debug = … is compile error VBC00001 Syntax error, as is any use of Debug other than its own statements (RD-VBAL §5.4.2.23 Assert Statement).

The language server reads the attribute off the module, with the module's other directives (ModuleDirectives.PredeclaredId, beside .Creatable and .Extensible), and the environment host is told it when the module's symbols are defined. The host defines the default instance then, before the code of any module is loaded, so that the code of every module can name it.

Runtime Semantics

What the code does What happens
Refers to the class's name for the first time The reference creates the object, and runs its Class_Initialize.
Refers to it again The same object. Class_Initialize runs once.
Widget Is Nothing False: the default instance is never Nothing when it is referred to.
Widget.Size = 3, then Widget.Size 3: the default instance is a variable, and what is assigned to its members stays with it.
Widget.Create(42), a member of the class A call on the default instance, like a call on any other object of the class.
Me Is Widget, in the code of the class True in a member called on the default instance, False in one called on any other object: the usual guard against a member being called on the wrong one.
Set Widget = Nothing The default instance lets go of its object, whose Class_Terminate runs if nothing else holds it: its state is gone. The next reference to Widget creates another object, and runs Class_Initialize again, before anything sees the variable - Widget Is Nothing is still False.
Set Widget = New Widget The default instance holds the new object from then on.

The module of a predeclared class can be defined again, as a shell does for every line it reads; the default instance keeps the object it holds.

Semantic Flags

The semantic model of a class module (ModuleSemanticModel.Class) carries ClassModuleSemanticFlags, the facts an analyzer needs to tell a stateless default instance - the factory - from one that holds state (RD-VBAL §1.1.3 Core Semantic Flags). A standard module has none.

Flag The class module
DefaultInstance Is predeclared (VB_PredeclaredId = True).
Stateful Declares a module-level variable, or a member with a Static local: each object of the class holds state of its own.

A predeclared class with DefaultInstance and not Stateful has a stateless default instance: a call on it can't leave anything behind for the next one.

Note

🚧 TODO An Is Nothing check on a default instance reported as statically constant (False, RD-VBAL §5.6.9.7 Is Operator) is not implemented.

5.2.4.2 Implements Directive

Note

This section describes the implementation of MS-VBAL §5.2.4.2 Implements Directive.

The Implements directive specifies that the (class) module implements an interface class (RD-VBAL §3.1 Attributes and Directives).

If a class module specifies any Implements directives, the interfaces specified by those directives are included in the class type's SuperTypes array (VBClassType; RD-VBAL §2.4.2 Non-intrinsic Types).

A class module also implements Class implicitly, which is among its implemented interfaces without a directive (RD-VBAL §5.3.1.10). The directives below are the ones the source writes: its ImplementedInterfaceNames.

Static Semantics

ImplementsSemantics checks the directives of a class module and what they require of it.

  • The class a directive names must exist (VBC09311 otherwise, as for any type that does not), cannot be the class of the module itself, and cannot be named by more than one directive of the module. A class whose public variables or methods have an underscore in their names cannot be an interface class, and the implemented interface name prefix of an interface, its name and an underscore, cannot begin that of another: VBC09328.
  • The module must declare an implemented name declaration, InterfaceName_MemberName, for each public method of the interface class, of the same kind, and for each public variable the property accessors its declared type calls for: a Property Get and a Property Let, a Property Set in place of the Let when the variable is an Object or a class, and all three when it is a Variant: VBC09329. The Private members of the interface class are not part of its interface.

What an implemented name declaration must be is RD-VBAL §5.3.1.9.

What is wrong with a directive is reported where the directive is written: the class module symbol carries the range of each directive (VBClassModuleSymbol.ImplementedInterfaceRanges, one for each of the ImplementedInterfaceNames), and an interface the module does not implement completely is reported at the directive that names it. A symbol that was not read from source has no ranges, and is reported at the module.

A directive in an extensible module (VB_Extensible = True, RD-VBAL §3.1.1.8) is invalid: VBC09328.

The environment host learns of the directives from the language server: DefineSymbolsParams.ImplementedInterfaceNames carries the interface names a module's directives name, and the host composes the module's class symbol from them and from the members it has been sent (ISessionSymbols.TryComposeClassModule), resolving the interfaces again over every class module it knows whenever one is composed, so the order the modules are defined in does not matter.

Warning

Extensible ("document") modules cannot specify any Implements directives.

MS-VBA does not strictly enforce this rule. In MS-VBA, an Implements directive in an extensible module can cause host application instabilities, source project corruption, and host application crashes.

RD-VBA must explicitly and statically deny Implements directives in extensible modules, as a normal compile-time error.

5.2.4.3 Event Declaration

This section corresponds to MS-VBAL §5.2.4.3 Event Declaration.

An Event declaration defines an event member of the class module. It is a VBEventMemberSymbol: its name, and its parameters, which describe the arguments a RaiseEvent (RD-VBAL §5.4.2.20) must give and the parameter list a handler must have (RD-VBAL §5.3.1.8); it defines no variable. An Event without an access modifier is Public. VBClassModuleSymbol.Events lists them, and FindEvent finds one by name, without regard to case.

The event symbols and their parameters are among the member descriptors the environment host receives, so a class's events are known where RaiseEvent runs.

An event name must be unique within the class module, reported as a duplicate declaration (VBC09303) when it is not, and must not contain an underscore, which is what separates a handler's variable from its event: VBC09325. Both are reported by ClassModuleEventSemantics.


⏮️ RD-VBAL §5.2.3 Module Declarations | ⏭️ RD-VBAL §5.3 Module Code Section Structure