5.0 Semantics
The role of semantics is to encode the meaning of the language into a set of deterministic rules and specified sequences of operations.
RDCore.SDK defines two types of abstract semantics:
| Semantics | Defined by | Availability to the semantic analysis layer |
|---|---|---|
| Static semantics (§5.0.1) | IStaticSemantics | Fully available. |
| Runtime semantics (§5.0.2) | IRuntimeSemantics<,> | Partially available, for simulated execution pipelines. |
Static semantics are effective in design-time. Runtime semantics are generally unavailable in a static context.
The environment host may provide additional semantics through external providers (extensions; see RD-VBAL §1.1 Design and Extension Philosophy).
The same layered refinement through templated methods applies to all semantics, both static and runtime (RD-VBAL §3.3.0 Operator Expressions).
5.0.1 Static Semantics
The role of static semantics is to determine a declared type for a given bound expression, given the determined static declared type of its inputs.
Static semantics always yield a StaticSemanticsEvaluationResult:
| Result | Encapsulates |
|---|---|
Success |
A VBType: the declared type. |
Error |
A VBCompileErrorInfo: compile-time error metadata. |
👉 In most static-semantics error cases, the compile-time error metadata returned is for a
TypeMismatcherror (VBCompileErrorId).
Static evaluation context
Every static semantics rule is evaluated against a StaticEvaluationContext:
| Member | Description |
|---|---|
Resolver |
The ISymbolResolver. |
Scope |
The LexicalScope the expression is lexically found in. How a lexical scope is resolved is specified in RD-VBAL §2.3.1.3 Name Resolution. |
EnclosingWithTargetType |
The declared type of the innermost enclosing With block's target expression, or null when the expression is not inside any With block (RD-VBAL §5.6.15 With Expressions). |
Module-level facts a static semantics rule needs are not parameters of the StaticEvaluationContext. They live on
ModuleDirectives, reachable from any scope via
LexicalScope.EnclosingModuleDirectives().
The module-level fact a static semantics rule needs is whether the enclosing module declares Option Explicit
(RD-VBAL §5.2.1 Option Directives). ModuleDirectives also carries the
module's Option Compare mode, and a Strict member reserved for RD-VBA's '@OptionStrict annotation
(RD-VBAL §2.3.1.3 Name Resolution). No symbol provider sets Strict, so
it is always false, and nothing reads it.
Keeping module-level facts on ModuleDirectives rather than on StaticEvaluationContext keeps the context's shape
stable as the directive surface that MS-VBAL and RD-VBA both define (Option Compare, Attribute declarations, …)
grows.
Expression evaluation
Each static semantics rule for a node kind is documented in isolation, in the section that implements its MS-VBAL
counterpart. A node kind's own static semantics rule does not recurse into its own children to produce the
operandDeclaredTypes it is given.
ExpressionStaticSemanticsEvaluator is the component that recurses into child expressions, given any (possibly deeply nested) expression:
- It dispatches by the node's own type; for an operator node, it dispatches by the operator's token.
- It evaluates children first.
- It short-circuits on the first error.
With ExpressionStaticSemanticsEvaluator, an expression such as Foo.Bar.Baz or x + 1 resolves end to end.
| Case | Outcome in ExpressionStaticSemanticsEvaluator |
|---|---|
| A node kind with no static semantics rule | Defers to VBUnknownType rather than erroring. |
An operator token with no mapped rule, such as Mod (RD-VBAL §5.6.9.3 Arithmetic Operators) |
Defers to VBUnknownType rather than erroring. |
Every type-comparing static-semantics rule in RDCore follows the same convention: an unresolved declared type is deferred, not flagged (see VBC09320).
See RD-VBAL §5.6.10 Simple Name Expressions for the static semantics of a simple name expression.
5.0.2 Runtime Semantics
The role of runtime semantics depends on the type of node being evaluated:
| Node | At runtime |
|---|---|
| Directives | Evaluate to their static / compile-time value. |
| Literal or constant expressions | Evaluate to their static / compile-time value (RD-VBAL §5.6.5 Literal Expressions). |
| Operators | Evaluate a VBTypedValue from their operands (RD-VBAL §5.6.9 Operator Expressions). |
| Statements | Induce side-effects to program, global, or host environment state (RD-VBAL §5.4 Procedure Bodies and Statements). |
🎯 Evaluation returns an evaluation result record, RuntimeSemanticsEvaluationResult, that describes and encapsulates either the evaluation result or runtime error metadata (RD-VBAL §3.0.3 Binding Contexts).
See RD-VBAL §5.6.9.2 Simple Data Operators for the operator evaluation pipeline and computation in the effective type.
See RD-VBAL §5.6.9.3 Arithmetic Operators,
RD-VBAL §5.6.9.5 Relational Operators (including the Variant
String/Numeric comparison exception) and RD-VBAL §5.6.9.8 Logical Operators
for the evaluation of each operator family.
See RD-VBAL §5.5.1.2 Runtime semantics for let-coercion, including
numeric let-coercion and Variant let-coercion and storage.
See RD-VBAL §6.1.1 Predefined Enums (§6.1.1.16 VbVarType) for the
VarType and COM interop shape of a Variant.
See RD-VBAL §5.4 Procedure Bodies and Statements for statement evaluation.
5.0.3 Semantic Analysis
The analysis pipeline of all operators follows a fixed sequence of three steps:
- The effective type of the operation is determined, based on the declared type of its operands. This step invokes the same methods as runtime semantics to determine the effective type.
- Validation: all non-null operands (non-VBNullValue) are let-coerced to the determined effective type of the operation. This step uses the same runtime semantics let-coercion provider as the evaluation pipeline (RD-VBAL §5.5.1.2 Runtime semantics).
- Semantic evaluation: a templated method evaluates a semantic result, having the execution context and the validated operands to work with, without inducing any side-effects.
The Analyze method yields a builder,
ISemanticContextContributor<,>, that
builds a semantic context for the specific expression node. The semantic context includes the results of each
analysis step:
| Semantic context member | Encapsulates |
|---|---|
| DetermineOperatorEffectiveTypeResult | The result of the first step. |
| LetCoercionAnalysisContext | The aggregated evaluation stack and outcome of all let-coercion operations, with their respective semantic flags. |
RuntimeSemanticsEvaluationResult |
The result of the operation. |
The language core features an analytical pipeline that attaches detailed semantic flags to abstract syntax tree (AST) nodes (RD-VBAL §1.1.3 Core Semantic Flags).
👉 The role of the
Analyzemethod at this level is to report the semantic facts of an operation. These facts usually cannot be inferred from the operands or effective type alone.
Semantic flags are facts, not opinions.
The semantic model
What the analysis finds out about a procedure is described by an immutable ProcedureSemanticModel, and that of a module by a ModuleSemanticModel. A model is built by the pass that analyzed the code; it is never written back onto the syntax tree or onto a value.
The first fact a model holds is the compile errors of the static pass (RD-VBAL §5.0.1), which is one walk over a
procedure body: StatementStaticSemanticsEvaluator. Given the symbols of a workspace it evaluates every expression, and
the coercion of every assignment; with none, CheckStructure checks what needs no name resolution:
| Rule | Reported as |
|---|---|
An Exit statement is where it may be (MS-VBAL §5.4.2.5, .7, .17-.19). |
VBC09312–VBC09315, VBC09332 |
| A label is defined once (MS-VBAL §5.4.1.1). | DuplicateLabelDefinition |
| A jump names a label that is defined. | LabelNotDefined |
A statement exists in the language: a bare Print is a statement of BASIC only. |
SubOrFunctionNotDefined |
A module is not valid for having valid procedures: what it declares is checked once for the module, by
DeclarationStaticSemanticsEvaluator, and a ModuleSemanticModel holds those errors (DeclarationErrors) beside the
model of each procedure, so that it is valid only when both are.
| Rule | Reported as |
|---|---|
| A name is declared once in the scope of a module; the accessors of a property are the one declaration of it. | DuplicateDeclaration |
| A declared type is a name that resolves to a type (MS-VBAL §5.6.4): of a variable, constant, parameter, result or local. | UserDefinedTypeNotDefined: The declared type 'Missing' could not be resolved. |
What a class module declares about events (MS-VBAL §5.2.4.3, §5.2.3.1.2, §5.3.1.8). |
ClassModuleEventSemantics |
What its Implements directives require of it (MS-VBAL §5.2.4.2, §5.3.1.9). |
ImplementsSemantics |
An unknown type is a type that is not known yet; a name that did not resolve is kept as one (VBUnresolvedType, which is an
unknown type in every other respect) so that the error can say which. The host defines a module at a time, and a name that
does not resolve while the module that declares it is defined may name a module defined after it, so the rule is asked for
(DeclarationRules.DeclaredTypes) only when everything the declaration can see is defined: the name is then resolved again,
and is an error if it still does not.
Expression facts
Given the symbols of a workspace, the static pass records an
ExpressionFact for every expression it evaluates, in the
Expressions of the procedure's model, by the expression's node identity. An operand has a fact of its own, evaluated before
the expression that has it.
| Member | Is |
|---|---|
DeclaredType |
The declared type of the expression (RD-VBAL §5.0.1); null when it is an error, which Error then holds. |
Classification |
What it names (MS-VBAL §5.6.1): a value, variable, constant, function, property, subroutine, type, namespace, or the member of an object that is bound when it runs. |
Binding |
The SemanticId of the symbol it refers to, when it resolved to one. |
Flags |
ValueExpressionSemanticFlags: Literal, LateBound, DefaultMember, WithBlockRelative, DictionaryAccess, ProcedureCall, CaseMismatch, and ExplicitCallKeyword on the callee of a Call statement written with the keyword. |
An expression an assignment writes to, the counter or control variable of a For or For Each, the string a Mid statement
replaces a part of, and the array a ReDim gives its dimensions, is flagged AssignmentTarget; an element of an array is
written through the array. What a statement prints is an expression like any other, evaluated and described as one.
Facts are descriptions, not opinions: whether a late-bound member, a name written in another case or the obsolete Call keyword
is worth a diagnostic is for an analyzer to say.
Declaration facts
A ModuleSemanticModel also describes the declarations of the module (Declarations,
DeclarationFact): each variable, constant, parameter, procedure, property
and event, with the access it is declared with and where. A variable that was never declared is IsImplicit (it came into
being because something referred to it), and OptionExplicit says whether the module states Option Explicit
(MS-VBAL §5.2.1.3); it is not issued (null) for a language that has no such directive, such as the platform's BASIC. The
accessors of a property are one declaration.
A fact is stated only when it is true. A fact that says a declaration is not used is a claim about every place that
could use it, and the language core makes it only when it can vouch for all of them. A declaration's References (the
expressions that read it, write to it, and pass it as an argument that may be taken by reference) are stated when both hold,
and are null otherwise, which says the references are not known, and not that there are none:
- Nothing outside the code analyzed can refer to it: a local, a parameter, or a variable that is not
PublicorFriend. A procedure, a property and an event are also called by convention (an event handler, a member that implements an interface) or by name at run time, and a constant is referred to by the expressions of declarations (the bounds of an array, the value of another constant), which are not evaluated as those of a procedure are: none of them has references yet. - The code that could refer to it was analyzed completely (
ProcedureSemanticModel.IsFullyAnalyzed): the procedure has no error, and the body was looked at again, apart from the pass, for each place a name is written that refers to something, and every one has a fact. For a local or a parameter that is its procedure; for a variable of the module, every procedure.
An analyzer reads what is stated, and has nothing to say about what is not. Which declarations are worth a diagnostic is its to say.
A statement inside an excluded #If branch is not analyzed and defines no label (MS-VBAL §3.4.2). Lowering a body to
instructions (RD-VBAL §3.5.2 Instruction) reports exactly these errors, by calling
CheckStructure: the rules are written in one place, and lowering only acts on the outcome (a jump that lands nowhere has no
target; an Exit that is not where it may be has no instruction).
Diagnostics
🧩 The role of analyzers in extensions like RDCore.Diagnostics is to inspect the flags and errors in semantic contexts, and issue diagnostics (RD-VBAL §1.1.4 Core Diagnostics).
| Diagnostic | Use |
|---|---|
| Error | Reserved for coded syntax/compilation and runtime/application errors. |
| Warning | Used carefully: for flagging potential bugs or logical errors causing unexpected or unintended behavior, or severe performance issues. |
| Hint or suggestion | May be as opinionated as needed. |
A warning severity must account for a treat warnings as errors host environment configuration setting: a diagnostic that should not break a build is not a warning (RD-VBAL §2.6 Diagnostics).
Type coercion
RDCore implements the MS-VBAL type-coercion rules through pattern-matching against its type system (RD-VBAL §5.5 Implicit coercion).
The rules are implemented verbatim, except for the resolved specification errors noted in RD-VBAL §5.5.1.2 Runtime semantics. Divergences from MS-VBAL caused by obvious copy/paste and transcription errors in the MS specification are resolved in favour of the evident intent.
Anything in MS-VBAL that implicitly depends on the Windows Registry, ActiveX, or MSForms is out of scope for the RD-VBA run-time; such requirements are also resolved in favour of the evident intent.
In this section
| § | Title | MS-VBAL |
|---|---|---|
| 5.1 | Module Body Structure — reserved | §5.1 |
| 5.2 | Module Declaration Section Structure — reserved | §5.2 |
| 5.3 | Module Code Section Structure — reserved | §5.3 |
| 5.4 | Procedure Bodies and Statements | §5.4 |
| 5.5 | Implicit coercion | §5.5 |
| 5.6 | Expressions — reserved | §5.6 |
⏮️ RD-VBAL §4.1 VBIDE Synchronization | ⏭️ RD-VBAL §5.1 Module Body Structure