ETS2Panda AST Verifier Knowledge Base
v1.0 | 2026-06-01 | Scope:
ets_frontend/ets2panda/ast_verifier/Not covered: parser AST definitions (see../parser/), lowering (see../lowering/), checker type logic (see../checker/), codegen (see../compiler/). Read before modifying:ast_verifier/AGENTS.md,ast_verifier/README.md.
Core Model
Read-only. Debug-only. Phase-string-driven. A validation gate between lowering and codegen. Never mutates AST. Runs only in debug builds. Invariants activate progressively via exact phase string matching (IntroduceNewInvariants("PhaseName")).
- Invoke:
compiler/core/compilerImpl.cpp::RunVerifierAndPhases;IntroduceNewInvariants("PhaseName")enables invariants progressively. Phase matching is by exact string. - Hard gate: Destructor asserts
!HasErrors() && !HasWarnings().After()MUST be called to dump messages before destruction; skippedAfter()= assertion crash with zero diagnostic output. - Traversal:
SinglePassVerifierwalks all nodes. Each invariant sees every node. Bothallowed_(phase-gated) andenabled_(CLI-configured) must be true. - Dependency ordering:
RequiredInvariants<ENUM, Deps...>enforces at compile time that dependencies precede dependents in the registry.
Activation groups (see constructor + IntroduceNewInvariants calls in compiler/core/compilerImpl.cpp for the canonical list). Guardrail: invariants in Base group MUST NOT depend on types or scopes — those go in CheckerPhase / ScopesInitPhase groups. Dependencies are enforced at compile time via RequiredInvariants<ENUM, Deps...>.
Constraints & Traps
ASTVerifier (entry point)
| Constraint | Detail |
|---|---|
| Destructor is a hard gate | After() must be called before destruction. If skipped, assertion fires with zero diagnostic info. |
| Phase string matching is exact | Typos, renames, or reordering silently break activation. Verify against compiler/lowering/phase.cpp::GetETSPhaseList(). |
InvariantsRegistry order == enum order |
Mismatch = compile error. Both derived from util/options.yaml. |
Traversal depends on Iterate |
Custom Iterate that skips children = unverified children. Iterate that visits extra nodes = false positives. |
Invariants (all invariants/*)
| Constraint | Detail |
|---|---|
| Must NOT self-iterate AST | SinglePassVerifier handles traversal. Self-iteration = O(N^2). |
| Must NOT call other invariants | Use RequiredInvariants + Get<Dep>(). Direct calls bypass allowed_/enabled_ gating. |
Must return {CORRECT, CONTINUE} for non-applicable nodes |
Framework contract. Any other return breaks skip-tree logic. |
| Must never crash | Null-check before As<T>(), TsType(), Variable(), Parent(). |
SKIP_SUBTREE scope: children only |
Re-enables after subtree. Incorrect use silences child violations. |
| No per-node data structure allocation | All invariants run on every node. O(N) per-invariant = O(N^2) total. |
Helpers (helpers.h/helpers.cpp)
| Function | Trap |
|---|---|
IsContainedIn |
Returns false silently on nullptr args |
TryGetLocalScopeVariable |
LOCAL flag does not guarantee valid scope |
ValidateVariableAccess |
Must check HasFlag(PROPERTY) first |
IsValidTypeForBinaryOp |
Must check IsTyped() first |
GetClassDefinitionType / GetTSInterfaceDeclarationType |
Returns nullptr for nodes not inside a class/interface |
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| Disabling/weakening a check to pass a patch | Safety-net gap | Fix the AST producer |
| Type/scope-dependent invariant in Base group | Null crash | Use CheckerPhase / ScopesInitPhase |
| Invariant self-iterates AST children | O(N^2) | Rely on SinglePassVerifier |
| Enum+registry mismatch | Compile error | Add to both options.yaml + ASTVerifier.h |
Typo in IntroduceNewInvariants phase name |
Silent deactivation | Verify against GetETSPhaseList() |
| Exceptions masking producer bugs | Masks root cause | Fix upstream; mark TODO |
Destructor fires without After() |
Zero-info crash | Always call After() |
Human Approval Required
- Disable/weaken an invariant (comment out, severity change, suppress default)
- Remove/weaken destructor assertions (
ES2PANDA_ASSERT) - Reorder
VerifierInvariantsenum without reordering registry - Add non-canonical phase names to
IntroduceNewInvariants - Modify
SinglePassVerifiertraversal logic orInvariantBase/InvariantsRegistryImpltemplates
Adding a New Invariant — Minimum Commit Flow
- Add enum entry in
util/options.yaml(ast-verifiersection) + assign to activation group. - Register in
ASTVerifier.h(InvariantsRegistry) in same order as enum. - Create
invariants/YourCheck.h+.cpp: inheritInvariantBase, implementCheck(). Return{CORRECT, CONTINUE}for non-applicable nodes. Never self-iterate. - If reading another invariant's data, declare
RequiredInvariants<YOUR_ENUM, DepEnum>. - Add to both
CMakeLists.txtandBUILD.gn. - Add unit test:
test/unit/public/ast_verifier_yourcheck.cpp(positive + negative case). - Verify phase name matches
compiler/lowering/phase.cpp::GetETSPhaseList()exactly. Typo = silent deactivation.
Active invariant count is ~18 (may drift — check invariants/ directory for current count).
Pre-Modification Checklist
- Read
ast_verifier/AGENTS.mdandast_verifier/README.md - New invariant registered in all four:
options.yaml(enum + group),ASTVerifier.h(registry),CMakeLists.txt,BUILD.gn - Dependencies declared via
RequiredInvariantsif the invariant reads another's data - Activation group matches data dependencies (no type checks before CheckerPhase, no scope checks before ScopesInitPhase)
- Returns
{CORRECT, CONTINUE}for non-applicable nodes - Phase name exact match to
compiler/lowering/phase.cpp::GetETSPhaseList() - Unit test with positive case (valid AST passes) and negative case (violation detected)
Code Anchors
Read order for understanding the verifier: compilerImpl.cpp (invocation) → ASTVerifier.h (registry + traversal) → invariantBase.h (CheckDecision, RequiredInvariants) → invariants/*.h (individual checks).
| What | Where |
|---|---|
| Verifier invoke + activation | compiler/core/compilerImpl.cpp (RunVerifierAndPhases) |
| Main class (registry, traversal) | ast_verifier/ASTVerifier.h, ASTVerifier.cpp |
| Base types, CheckDecision, RequiredInvariants | ast_verifier/invariantBase.h |
| Invariant checks (19 classes, 18 active) | ast_verifier/invariants/*.h, *.cpp |
| Shared utility predicates | ast_verifier/helpers.h, helpers.cpp |
| CLI options, enum definitions | util/options.yaml (section ast-verifier) |
| Canonical phase names | compiler/lowering/phase.cpp (GetETSPhaseList()) |
| Unit tests (16 files) | test/unit/public/ast_verifier_*.cpp |
| Test base class | test/utils/ast_verifier_test.h |
Build & Verification
Build (from repository root):
cmake --build <out_dir> -j8 # es2panda including ast_verifier
Test commands:
# AST checker / verifier unit tests
runtime_core/static_core/tests/tests-u-runner-2/runner.sh es2panda-verifier astchecker --extension=ets --load-runtimes=ets --force-generate --processes=all
# Parser tests
runtime_core/static_core/tests/tests-u-runner-2/runner.sh es2panda-verifier parser --extension=ets --load-runtimes=ets --force-generate --processes=all
# Runtime tests
runtime_core/static_core/tests/tests-u-runner-2/runner.sh es2panda-verifier ets-runtime --extension=ets --load-runtimes=ets --force-generate --processes=all --es2panda-args=--simultaneous=true
# CTS tests
runtime_core/static_core/tests/tests-u-runner-2/runner.sh es2panda-verifier ets-cts --extension=ets --load-runtimes=ets --force-generate --processes=all --es2panda-args=--simultaneous=true
New files must be added to both CMakeLists.txt and BUILD.gn.
Symptom -> Check
| Symptom | Check |
|---|---|
| Verifier rejects valid AST after lowering | Isolate --ast-verifier:phases=<Phase>. New pattern not in ExceptionsMatcher. |
| Null dereference crash | Missing Is<T>() before As<T>(); null TsType()/Variable()/Parent(). |
| New invariant never fires | Node filter excludes target; allowed_ false; wrong phase group. |
IntroduceNewInvariants silent failure |
Phase name mismatch. Verify against GetETSPhaseList(). |
| Messages leak between source files | Init() not called or override doesn't clear all state. |
Related Documents
ets2panda/ast_verifier/AGENTS.md-- development rules and directory-level constraintsets2panda/ast_verifier/README.md-- CLI options, adding-invariant guidedocs/ets2panda/Static_Frontend_Knowledge_Base.md-- covers all upstream/downstream stages: AST nodes (IR), parser, checker (type data), lowering (AST producer), compiler core (runs after verifier)AGENTS.md-- build/test expectations and repository-level guidance