rucc_base/scope.rs
1//! A map from a name to a value, kept in a stack of scopes.
2//!
3//! Design: `spec/06-lexer-and-parser.md` section 6.4.
4//!
5//! Two passes need this and they need the same thing from it. The parser holds one per C
6//! namespace to answer whether an identifier in a specifier list is a type name, which is the
7//! one real ambiguity in C's grammar. Semantic analysis holds one per namespace to resolve a
8//! use of a name to the declaration it refers to. Neither of them knows anything about the
9//! other's values, so what is shared is the scoping and not what is scoped, and it lives here
10//! rather than being written twice and drifting.
11//!
12//! Nothing in here knows what C is. It is a name, a value, and the rule that an inner binding
13//! hides an outer one until its scope closes.
14
15use crate::hash::Map;
16use crate::intern::Symbol;
17
18/// One binding, and the scope it was made in.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20struct Binding<V> {
21 depth: u32,
22 value: V,
23}
24
25/// A map from a name to a value, in a stack of scopes.
26///
27/// A lookup has to find the innermost binding of a name, and a scope closing has to expose
28/// whatever that name meant outside it. Walking a stack of scopes would make every lookup cost
29/// the depth, and a compiler looks up every identifier it reads, so the shape is inverted: one
30/// map from name to the stack of bindings for that name, innermost last, plus a log of the
31/// names bound in each open scope so that closing one knows what to undo.
32#[derive(Debug)]
33pub struct ScopeMap<V> {
34 /// The bindings of each name, innermost last. An empty stack means the name is not bound,
35 /// and the entry is kept rather than removed so that the allocation is reused by the next
36 /// declaration of that name, which in a header is usually the same names again.
37 bindings: Map<Symbol, Vec<Binding<V>>>,
38 /// Every name bound in an open scope, in the order it was bound.
39 log: Vec<Symbol>,
40 /// Where each open scope starts in `log`. The file scope is not in here, which is what
41 /// makes it impossible to close.
42 marks: Vec<usize>,
43}
44
45impl<V> Default for ScopeMap<V> {
46 fn default() -> Self {
47 ScopeMap { bindings: Map::default(), log: Vec::new(), marks: Vec::new() }
48 }
49}
50
51impl<V: Copy> ScopeMap<V> {
52 /// An empty namespace, with the file scope open.
53 #[must_use]
54 pub fn new() -> Self {
55 ScopeMap::default()
56 }
57
58 /// How many scopes are open. The file scope counts, so this is never zero.
59 #[inline]
60 #[must_use]
61 pub fn depth(&self) -> u32 {
62 // The count is bounded by the nesting the parser accepted, which is capped long before
63 // this could overflow.
64 self.marks.len() as u32 + 1
65 }
66
67 /// Whether the only open scope is the file scope.
68 #[inline]
69 #[must_use]
70 pub fn at_file_scope(&self) -> bool {
71 self.marks.is_empty()
72 }
73
74 /// Opens a scope.
75 pub fn push(&mut self) {
76 self.marks.push(self.log.len());
77 }
78
79 /// Closes the innermost scope, exposing whatever its names meant outside it.
80 ///
81 /// # Panics
82 ///
83 /// Panics on closing the file scope, which nothing in C does and which would leave the
84 /// namespace unable to hold a declaration.
85 pub fn pop(&mut self) {
86 let mark = self.marks.pop().expect("the file scope is never closed");
87 while self.log.len() > mark {
88 let name = self.log.pop().expect("the log is longer than the mark");
89 if let Some(stack) = self.bindings.get_mut(&name) {
90 stack.pop();
91 }
92 }
93 }
94
95 /// Binds `name` in the innermost scope, and gives back what it was already bound to *in
96 /// that same scope*.
97 ///
98 /// A returned value is a redeclaration, which is the caller's to judge: `int x; int x;` is
99 /// fine at file scope and `typedef int T; T T;` is not, and neither decision belongs here.
100 /// Shadowing an outer binding is not a redeclaration and gives back [`None`].
101 pub fn declare(&mut self, name: Symbol, value: V) -> Option<V> {
102 let depth = self.depth();
103 let stack = self.bindings.entry(name).or_default();
104 match stack.last_mut() {
105 Some(top) if top.depth == depth => {
106 let was = top.value;
107 top.value = value;
108 Some(was)
109 }
110 _ => {
111 stack.push(Binding { depth, value });
112 self.log.push(name);
113 None
114 }
115 }
116 }
117
118 /// Binds `name` in the file scope from wherever the caller is, and answers whether it took.
119 ///
120 /// For the declaration a program did not write. A builtin used inside a function is
121 /// declared where C says the implementation declared it, which is the file scope, so that
122 /// what it means does not change when the block it was first used in closes.
123 ///
124 /// It takes only when the name is bound nowhere, which is the caller's own condition: this
125 /// is reached because a lookup found nothing. A name bound anywhere is left alone rather
126 /// than bound underneath, because the binding it already has may be the one being closed
127 /// over and this has no log entry to undo.
128 pub fn declare_at_file_scope(&mut self, name: Symbol, value: V) -> bool {
129 let stack = self.bindings.entry(name).or_default();
130 if !stack.is_empty() {
131 return false;
132 }
133 // Not logged, which is what makes it survive every scope that closes over it. The log
134 // is what a `pop` undoes, and the file scope is below the first mark and so is never
135 // undone whether it is logged or not.
136 stack.push(Binding { depth: 1, value });
137 true
138 }
139
140 /// What `name` is bound to in the innermost scope that binds it.
141 #[must_use]
142 pub fn get(&self, name: Symbol) -> Option<V> {
143 Some(self.bindings.get(&name)?.last()?.value)
144 }
145
146 /// What `name` is bound to in the innermost scope that binds it to something `wanted` takes.
147 ///
148 /// The walk goes outwards from the innermost binding. `extern int v;` is what asks for this:
149 /// C 6.2.2p4 hands it the linkage of a visible prior declaration only where the prior
150 /// declaration has a linkage of its own, so the search has to carry on past a local of the
151 /// same name rather than stop at it.
152 #[must_use]
153 pub fn get_where(&self, name: Symbol, wanted: impl Fn(V) -> bool) -> Option<V> {
154 let stack = self.bindings.get(&name)?;
155 stack.iter().rev().map(|binding| binding.value).find(|&value| wanted(value))
156 }
157
158 /// What `name` is bound to in the innermost scope alone, ignoring the ones outside it.
159 #[must_use]
160 pub fn get_here(&self, name: Symbol) -> Option<V> {
161 let depth = self.depth();
162 let top = self.bindings.get(&name)?.last()?;
163 (top.depth == depth).then_some(top.value)
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 /// A symbol the interner would have handed out. Nothing here reads a spelling.
172 const X: Symbol = Symbol::from_raw(1);
173
174 #[test]
175 fn only_the_innermost_scope_counts_as_here() {
176 let mut names = ScopeMap::new();
177 names.declare(X, 1);
178 names.push();
179 assert_eq!(names.get(X), Some(1));
180 assert_eq!(names.get_here(X), None);
181 assert_eq!(names.depth(), 2);
182 names.pop();
183 assert_eq!(names.get_here(X), Some(1));
184 }
185
186 #[test]
187 fn an_inner_binding_hides_an_outer_one_and_gives_it_back() {
188 let mut names = ScopeMap::new();
189 names.declare(X, 1);
190 names.push();
191 assert_eq!(names.declare(X, 2), None);
192 assert_eq!(names.get(X), Some(2));
193 names.pop();
194 assert_eq!(names.get(X), Some(1));
195 }
196
197 #[test]
198 fn a_second_binding_in_one_scope_is_a_redeclaration_and_says_what_it_was() {
199 let mut names = ScopeMap::new();
200 assert_eq!(names.declare(X, 1), None);
201 assert_eq!(names.declare(X, 2), Some(1));
202 assert_eq!(names.get(X), Some(2));
203 }
204
205 #[test]
206 fn a_file_scope_binding_made_from_inside_outlives_the_block_it_was_made_in() {
207 let mut names = ScopeMap::new();
208 names.push();
209 names.push();
210 assert!(names.declare_at_file_scope(X, 1));
211 assert_eq!(names.get(X), Some(1));
212 names.pop();
213 names.pop();
214 assert!(names.at_file_scope());
215 assert_eq!(names.get(X), Some(1), "the block it was used in is not where it was bound");
216 assert_eq!(names.get_here(X), Some(1));
217 }
218
219 #[test]
220 fn a_name_that_already_means_something_is_left_meaning_it() {
221 let mut names = ScopeMap::new();
222 names.push();
223 names.declare(X, 1);
224 assert!(!names.declare_at_file_scope(X, 2));
225 assert_eq!(names.get(X), Some(1));
226 names.pop();
227 assert_eq!(names.get(X), None);
228 }
229
230 #[test]
231 fn closing_a_scope_leaves_nothing_behind() {
232 let mut names = ScopeMap::new();
233 assert!(names.at_file_scope());
234 for depth in 0..64 {
235 names.push();
236 names.declare(X, depth);
237 }
238 assert_eq!(names.depth(), 65);
239 for _ in 0..64 {
240 names.pop();
241 }
242 assert!(names.at_file_scope());
243 assert_eq!(names.get(X), None);
244 }
245
246 #[test]
247 #[should_panic(expected = "the file scope is never closed")]
248 fn the_outermost_scope_cannot_be_closed() {
249 ScopeMap::<u32>::new().pop();
250 }
251}