Skip to main content

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