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
//! `let` define-before-use (IEEE 1800-2023 11.12).
//!
//! "In the scope of declaration, a let body shall be defined before it is
//! used." slang substitutes a `let` at its use site and accepts a
//! call-form forward use (`early(a)` before `let early(x) = ...;`); only the
//! no-argument form, which is an ordinary identifier lookup, is rejected
//! ("use of undeclared identifier"). This pass restores the rule for the call
//! form, on the syntax trees: every invocation of a bare identifier is
//! resolved the way a lexical lookup would be -- innermost enclosing scope
//! first -- against the `let` declarations of that name directly in each
//! scope, and one that is declared later in source order than the use is an
//! error.
//!
//! Source order is the pre-order position in the tree (not a buffer offset:
//! an `include`d file lives in another buffer, whose offsets are
//! incomparable with the includer's).
use sv_lang::kinds::SyntaxKind;
use sv_lang::nodes::{InvocationExpressionSyntax, LetDeclarationSyntax};
use sv_lang::{AstNode, Node, SyntaxTree};
/// One `let` declaration: its name, the scope node it is declared directly in
/// and its pre-order position.
struct LetDecl<'t> {
name: String,
scope: Node<'t>,
at: usize,
}
/// `Ok(())` if no call of a `let` precedes that `let`'s declaration in its
/// scope; otherwise the error text naming the `let` and the use's line.
pub fn check_let_definition_order(trees: &[SyntaxTree]) -> Result<(), String> {
for tree in trees {
check_tree(tree)?;
}
Ok(())
}
fn check_tree(tree: &SyntaxTree) -> Result<(), String> {
let mut lets: Vec<LetDecl<'_>> = Vec::new();
// `(callee name, the invocation, pre-order position)`.
let mut calls: Vec<(String, Node<'_>, usize)> = Vec::new();
for (at, node) in tree.root().descendants().enumerate() {
// Only these two kinds matter; every other node is irrelevant to the
// rule (not an unhandled case), hence `if` rather than a `match`.
let kind = node.kind();
if kind == SyntaxKind::LetDeclaration {
let Some(decl) = <LetDeclarationSyntax<'_> as AstNode>::cast(node) else {
continue;
};
let (Some(id), Some(scope)) = (decl.identifier(), node.parent()) else {
continue;
};
lets.push(LetDecl {
name: id.text().to_string(),
scope,
at,
});
} else if kind == SyntaxKind::InvocationExpression {
let Some(inv) = <InvocationExpressionSyntax<'_> as AstNode>::cast(node) else {
continue;
};
let callee = inv.left().syntax();
if callee.kind() != SyntaxKind::IdentifierName {
continue;
}
let Some(tok) = callee.first_token() else {
continue;
};
calls.push((tok.text().to_string(), node, at));
}
}
if lets.is_empty() {
return Ok(());
}
for (name, call, at) in &calls {
let mut scope = call.parent();
while let Some(s) = scope {
let mut here = lets.iter().filter(|l| l.name == *name && l.scope == s);
if let Some(first) = here.next() {
// Declared in this scope: the lookup stops here, whichever
// way it goes.
let earliest = here.fold(first.at, |m, l| m.min(l.at));
if earliest > *at {
return Err(format!(
"let `{name}` is used{} before its declaration: a let body shall be \
defined before it is used (IEEE 1800-2023 11.12)",
line_of(tree, call, name)
));
}
break;
}
scope = s.parent();
}
}
Ok(())
}
/// ` at line N` for the invocation `call` of `name`, when its buffer offset
/// is in the tree's own text; empty otherwise (an included buffer).
fn line_of(tree: &SyntaxTree, call: &Node<'_>, name: &str) -> String {
let text = tree.text();
let start = call.byte_range().start;
match text.get(start..) {
Some(rest) if rest.trim_start().starts_with(name) => {
let line = text[..start].bytes().filter(|&b| b == b'\n').count() + 1;
format!(" at line {line}")
}
_ => String::new(),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn check(src: &str) -> Result<(), String> {
let session = sv_lang::Session::new();
let tree = session.parse(src).expect("parse");
check_let_definition_order(&[tree])
}
#[test]
fn call_before_declaration_is_rejected() {
let err = check(
"module m;\n logic a;\n initial if (early(a)) $display(1);\n let early(x) = !x;\nendmodule\n",
)
.unwrap_err();
assert!(
err.contains("let `early` is used at line 3 before its declaration"),
"{err}"
);
}
#[test]
fn call_after_declaration_is_accepted() {
check("module m;\n logic a;\n let early(x) = !x;\n initial if (early(a)) $display(1);\nendmodule\n")
.unwrap();
}
#[test]
fn an_inner_let_shadows_a_later_outer_one() {
// The use finds the inner declaration (earlier than the use); the
// outer let of the same name is declared after, but never reached.
check(
"module m;\n logic a;\n initial begin\n let f(x) = x;\n if (f(a)) $display(1);\n end\n let f(x) = !x;\nendmodule\n",
)
.unwrap();
}
#[test]
fn a_call_of_a_name_that_is_not_a_let_is_ignored() {
check(
"module m;\n logic a;\n initial if (f(a)) $display(1);\n function logic f(logic x); return x; endfunction\n let g(x) = x;\nendmodule\n",
)
.unwrap();
}
#[test]
fn a_later_let_in_a_sibling_scope_is_not_a_forward_use() {
check(
"module m;\n logic a;\n initial begin : b1\n if (f(a)) $display(1);\n end\n initial begin : b2\n let f(x) = x;\n end\nendmodule\n",
)
.unwrap();
}
}