1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! Task 29 (CG-069): CI-pinned `Kind::COUNT` constants for the three
//! `sv-lang-kinds` AST-node discriminant enums this crate's own exhaustive
//! matches (`expr.rs::lower_expr`'s `ExpressionKind` match, `func.rs::
//! lower_stmt`'s `StatementKind` match, and this task's new `session::
//! symbol_role`'s `SymbolKind` match) fall back to a hard [`crate::error::
//! LoweringError`] for once every explicitly-handled variant is exhausted.
//!
//! Each constant is a plain hand-pinned `usize`, not re-derived from the
//! live `sv_lang::kinds::*Kind::COUNT` associated constant at build time --
//! doing that would make this file's own tests
//! (`expression_kind_count_matches_pinned_constant` and its two siblings,
//! `tests/exhaustiveness.rs`) compare a constant against itself, which can
//! never disagree and is exactly the "gate that cannot fail" this project's
//! correctness posture forbids. Pinned against the vendored
//! `sv-lang-kinds` version this workspace's `Cargo.toml` pins with `=`
//! (`sv-lang-kinds = "=0.1.1"`, resolving to `0.1.1+slang.11.0` per
//! `Cargo.lock`), read directly off that crate's own generated
//! `src/generated/ast_kind.rs` (`ExpressionKind::COUNT = 41`,
//! `StatementKind::COUNT = 32`, `SymbolKind::COUNT = 101`) -- a future
//! `sv-lang-kinds` version bump that adds, removes, or renumbers a variant
//! changes that generated file's own `COUNT`, which shows up here as this
//! file's tests failing loudly at CI time, never as a silent stale pin.
//!
//! **`TimingControlKind` is deliberately NOT pinned here** (see Task 29's
//! own plan note, `docs/superpowers/plans/2026-09-23-wave-A-codegen.md`):
//! no code anywhere in `crates/sv-sim-codegen`'s `expr.rs`/`func.rs`/
//! `session.rs` exhaustively dispatches over it. `module.rs::
//! require_single_edge_head`'s `if tc.kind() != TimingControlKind::
//! SignalEvent` is a single-variant equality guard, not an exhaustive
//! dispatch, and that file already carries its own, narrower-scoped pin
//! (`module.rs::assert_kind_counts_match_pinned_slang`) for the matches it
//! itself owns. Wave A2's CPS process-lowering machinery is the only
//! subsystem that will ever have a genuine exhaustive `match` over
//! `TimingControlKind` to protect -- pinning `TIMING_CONTROL_KIND_COUNT`
//! here with nothing behind it would itself be a gate that cannot fail.
/// Pinned `sv_lang::kinds::ExpressionKind::COUNT` (41 variants as of
/// `sv-lang-kinds` 0.1.1+slang.11.0) -- guards `expr.rs::lower_expr`'s
/// exhaustive dispatch.
pub const EXPRESSION_KIND_COUNT: usize = 41;
/// Pinned `sv_lang::kinds::StatementKind::COUNT` (32 variants as of
/// `sv-lang-kinds` 0.1.1+slang.11.0) -- guards `func.rs::lower_stmt`'s
/// exhaustive dispatch.
pub const STATEMENT_KIND_COUNT: usize = 32;
/// Pinned `sv_lang::kinds::SymbolKind::COUNT` (101 variants as of
/// `sv-lang-kinds` 0.1.1+slang.11.0) -- guards `session::symbol_role`'s
/// exhaustive dispatch.
pub const SYMBOL_KIND_COUNT: usize = 101;
/// R-run Task 19 (XC-053): the codegen-side half of the cross-track
/// `SupportedConstructs` interface ledger row XC-053 asks for -- "CODEGEN
/// and RUNTIME each expose an enumerable SupportedConstructs set ... that
/// sv-sim-harness's classify_via_frontend/CorpusManifest consumes to
/// pre-classify every corpus file as expected-pass, known-gap, or
/// unclassified, before any run."
///
/// **Deviation from the plan's literal derivation** (`docs/superpowers/
/// plans/2026-09-27-run-wiring.md` Task 19, "Public names/types"): the plan
/// describes deriving this set by walking `module.rs::
/// reject_unsupported_in_body`'s own "allow-list". Reading that function
/// (`module.rs:998`, its own doc comment) shows it is not an allow-list at
/// all -- it is a narrow DENY-list (SVA assertions, `randomize()` calls;
/// it also held `$sdf_annotate` until SD-B T25 made that a real call)
/// that lets every OTHER `StatementKind`/
/// `ExpressionKind` straight through to `process::cfg`'s own CFG-shape
/// enforcement (that function's doc comment says so explicitly: "loops,
/// `case`, `wait`, fork/join, ... deliberately NOT rejected here"). It
/// carries no information about which constructs actually lower to a real
/// value/effect, so walking it would make `SUPPORTED_CONSTRUCTS` wildly
/// over-report support.
///
/// The real registry with exactly the property XC-053 needs already exists:
/// [`crate::coverage::supported_constructs`] (CG-158/CG-159, Task 28 of a
/// different, already-merged track) -- a hand-maintained `&'static
/// [&'static str]` of every `ExpressionKind`/`StatementKind` variant name
/// whose match arm in `expr.rs::lower_expr`/`func.rs::lower_stmt` genuinely
/// produces a lowering (not merely a better error message for an
/// otherwise-rejected variant), kept from silently drifting by its own
/// `tests/coverage_sink.rs::supported_constructs_lists_every_implemented_
/// kind` regression guard. This function reuses that list -- never a
/// second, independently hand-typed enum-variant list, which is exactly the
/// "two implementations, one diverges" bug this workspace's own memory of
/// prior incidents warns about -- and turns it into the `HashSet<
/// ConstructId>` shape XC-053's acceptance text asks for, by matching each
/// string name against the real, pinned `sv_lang_kinds::{ExpressionKind,
/// StatementKind}::ALL` variant list via `Debug` formatting (both enums are
/// plain, data-less C-style variants, so `{:?}` is exactly the bare variant
/// name, e.g. `"HierarchicalValue"`, with no risk of a stringly-typed typo
/// silently matching nothing -- `supported_construct_ids_is_nonempty_and_
/// only_contains_kinds_lower_actually_accepts` below is the regression
/// proof that every name in `coverage::supported_constructs()` really does
/// resolve to a real `ConstructId`, not silently drop unmatched entries).
///
/// **Naming deviation**: the plan's own text spells this
/// `SUPPORTED_CONSTRUCTS()` (screaming-case). A screaming-case function name
/// fails this workspace's `cargo clippy --workspace --all-targets -- -D
/// warnings` gate (`clippy::non_snake_case` is warn-by-default and this
/// project denies all warnings) -- and `crate::coverage` already exports a
/// *different*-shaped `supported_constructs() -> &'static [&'static str]`,
/// so reusing that exact name here (even in `snake_case`) would shadow-name
/// two functions with different return types under the same identifier
/// across sibling modules of the same crate. Named `supported_construct_ids`
/// instead: still a plain, discoverable `pub fn`, unambiguous from
/// `coverage::supported_constructs` at every call site.