sv_sim_sim/patterns.rs
1//! Pattern matching (IEEE 1800-2023 12.6): what the analysis side needs.
2//!
3//! A pattern variable (`.n` in `tagged Valid .n`) is bound when its pattern
4//! matches and read by the later conditions and the matched branch. slang
5//! gives it no declaration statement and, for a `?:` pattern, no scope
6//! either, so the declared-locals scans (`process::liveness`, a subroutine's
7//! locals) cannot find it as a block member: [`pattern_vars`] finds them in
8//! the patterns themselves.
9
10use std::collections::HashSet;
11
12use sv_lang::{SemNode, Symbol};
13use sv_lang_kinds::{ExpressionKind, PatternKind, StatementKind};
14
15use crate::symid::{SymbolId, SymbolUid};
16
17/// Every pattern variable bound anywhere in the subtree of `root`
18/// (`if (e matches p)`, `case (e) matches`, `e matches p ? a : b`), once
19/// each, in source order. Variables are told apart by [`SymbolUid::uid`]; a
20/// `?:` pattern's variable lives in no scope, so two of them with the same
21/// name share a key (and a slot) -- codegen refuses the pair when their types
22/// differ in width.
23pub fn pattern_vars<'d>(root: SemNode<'d>) -> Vec<Symbol<'d>> {
24 let mut out = Vec::new();
25 let mut seen: HashSet<SymbolId> = HashSet::new();
26 collect(root, &mut out, &mut seen);
27 out
28}
29
30fn collect<'d>(root: SemNode<'d>, out: &mut Vec<Symbol<'d>>, seen: &mut HashSet<SymbolId>) {
31 let mut stack = vec![root];
32 while let Some(node) = stack.pop() {
33 let mut kids = node.children();
34 if let Some(p) = node.as_pattern()
35 && p.kind() == PatternKind::Variable
36 && let Some(v) = p.variable()
37 && !v.name().is_empty()
38 && seen.insert(v.uid())
39 {
40 out.push(v);
41 }
42 if let Some(s) = node.as_statement() {
43 // Only these two statements hold patterns; every other kind's
44 // children are already in `node.children()`.
45 if s.kind() == StatementKind::Conditional {
46 for c in s.conditions() {
47 kids.extend(c.pattern().map(|p| p.as_sem_node()));
48 }
49 } else if s.kind() == StatementKind::PatternCase {
50 for item in s.pattern_case_items() {
51 kids.push(item.pattern().as_sem_node());
52 kids.push(item.stmt().as_sem_node());
53 }
54 }
55 }
56 if let Some(e) = node.as_expression()
57 && e.kind() == ExpressionKind::ConditionalOp
58 {
59 for c in e.conditions() {
60 kids.extend(c.pattern().map(|p| p.as_sem_node()));
61 }
62 }
63 kids.reverse();
64 stack.extend(kids);
65 }
66}
67
68/// The case expression of a `case ... matches` statement. The bindings'
69/// `Statement::expr` has no `PatternCase` arm, so it is the first expression
70/// among the statement's semantic children (slang visits the case expression
71/// before the items); the other expression children are exactly the items'
72/// `&&&` filters, which is checked. `None` when `stmt` is not a `PatternCase`
73/// or the children do not have that shape.
74pub fn pattern_case_expr<'d>(stmt: sv_lang::Statement<'d>) -> Option<sv_lang::Expression<'d>> {
75 if stmt.kind() != StatementKind::PatternCase {
76 return None;
77 }
78 let exprs: Vec<_> = stmt
79 .as_sem_node()
80 .children()
81 .into_iter()
82 .filter_map(|c| c.as_expression())
83 .collect();
84 let filters = stmt
85 .pattern_case_items()
86 .iter()
87 .filter(|i| i.filter().is_some())
88 .count();
89 (exprs.len() == 1 + filters).then(|| exprs[0])
90}