Skip to main content

rucc_sema/
scope.rs

1//! The scopes semantic analysis keeps, and what a name means in each of them.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.4.
4//!
5//! The scoping itself is [`ScopeMap`], in `rucc-base`, because the parser keeps the same
6//! structure with different values in it. What is here is the values: C's namespaces, and what
7//! a name in each of them resolves to once the declaration it refers to has been checked.
8//!
9//! Two of C's four namespaces are here. Labels are function wide rather than block scoped, so
10//! the function checker holds them in a flat map and this stack would only be in the way.
11//! Members belong to the record that declares them and are reached through a type rather than
12//! through a scope, so they are a question for the type table.
13//!
14//! # Why the parser's answer is not enough
15//!
16//! The parser already resolved names, in the sense that it decided which of them were type
17//! names. That is a different question and a smaller one: it needed to know whether `A` in
18//! `(A)*b` was a type, and it never needed to know which `A`. This has to know which
19//! declaration a use refers to, because the answer is what the use gets its type from and what
20//! the object file eventually refers to.
21
22use rucc_base::{ScopeMap, Symbol};
23use rucc_types::TypeId;
24
25use crate::decl::DeclId;
26
27/// What an ordinary identifier names.
28///
29/// The four things C's ordinary namespace holds, which is objects, functions, typedef names and
30/// enumerators. The first two are the same case here because a use of either is a use of a
31/// declaration, and what separates them is the type it has.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum Binding {
34    /// An object or a function, which is a declaration in the typed tree.
35    Decl(DeclId),
36    /// A `typedef` name, which is a name for a type and never appears in the tree.
37    Typedef(TypeId),
38    /// An enumerator, which is a constant and is folded into the expression that used it.
39    Enumerator {
40        /// The value, in the enumeration's underlying type.
41        value: i128,
42        /// The type the constant has, which is the enumeration in C23 and `int` before it.
43        ty: TypeId,
44    },
45}
46
47/// Which keyword introduced a tag.
48///
49/// A mismatch is an error and the diagnostic has to name what was declared, so the three are
50/// kept apart rather than collapsed into the type they name.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum TagKind {
53    /// `struct`.
54    Struct,
55    /// `union`.
56    Union,
57    /// `enum`.
58    Enum,
59}
60
61impl TagKind {
62    /// How the keyword is spelled in a diagnostic.
63    #[must_use]
64    pub const fn as_str(self) -> &'static str {
65        match self {
66            TagKind::Struct => "struct",
67            TagKind::Union => "union",
68            TagKind::Enum => "enum",
69        }
70    }
71}
72
73/// A tag, and the type it names.
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct Tag {
76    /// Which keyword declared it.
77    pub kind: TagKind,
78    /// The type, which exists from the point the tag is first mentioned and is incomplete
79    /// until the definition is read.
80    pub ty: TypeId,
81}
82
83/// The scopes of one translation unit.
84#[derive(Debug, Default)]
85pub struct Scopes {
86    ordinary: ScopeMap<Binding>,
87    tags: ScopeMap<Tag>,
88}
89
90impl Scopes {
91    /// Empty scopes, with the file scope open.
92    #[must_use]
93    pub fn new() -> Scopes {
94        Scopes::default()
95    }
96
97    /// Opens a scope in every namespace.
98    ///
99    /// Both are pushed together because C opens them together. A parameter list is a scope of
100    /// its own, which is why the tag in `void f(struct S *p);` is gone by the next declaration,
101    /// and getting that wrong in one namespace and not the other is how the two drift.
102    pub fn push(&mut self) {
103        self.ordinary.push();
104        self.tags.push();
105    }
106
107    /// Closes the innermost scope in every namespace.
108    ///
109    /// # Panics
110    ///
111    /// Panics on closing the file scope.
112    pub fn pop(&mut self) {
113        self.ordinary.pop();
114        self.tags.pop();
115    }
116
117    /// Whether the only open scope is the file scope.
118    #[must_use]
119    pub fn at_file_scope(&self) -> bool {
120        self.ordinary.at_file_scope()
121    }
122
123    /// How many scopes are open, the file scope counting as one.
124    #[must_use]
125    pub fn depth(&self) -> u32 {
126        self.ordinary.depth()
127    }
128
129    /// Binds an ordinary identifier, and gives back what it was bound to in the same scope.
130    ///
131    /// A returned value is a redeclaration, which is the caller's to judge, since `int x; int
132    /// x;` is one object at file scope and an error inside a function.
133    pub fn declare(&mut self, name: Symbol, binding: Binding) -> Option<Binding> {
134        self.ordinary.declare(name, binding)
135    }
136
137    /// Binds an ordinary identifier in the file scope from wherever the checking is.
138    ///
139    /// For a builtin, which C says the implementation declared and which therefore was not
140    /// declared in whichever block first called it. Answers whether it took, which it does
141    /// only when nothing else binds the name.
142    pub fn declare_at_file_scope(&mut self, name: Symbol, binding: Binding) -> bool {
143        self.ordinary.declare_at_file_scope(name, binding)
144    }
145
146    /// What an ordinary identifier names here.
147    #[must_use]
148    pub fn lookup(&self, name: Symbol) -> Option<Binding> {
149        self.ordinary.get(name)
150    }
151
152    /// What an ordinary identifier names in the innermost scope that binds it to a binding
153    /// `wanted` takes, looking outwards.
154    #[must_use]
155    pub fn lookup_where(&self, name: Symbol, wanted: impl Fn(Binding) -> bool) -> Option<Binding> {
156        self.ordinary.get_where(name, wanted)
157    }
158
159    /// What an ordinary identifier names in the innermost scope alone.
160    #[must_use]
161    pub fn lookup_here(&self, name: Symbol) -> Option<Binding> {
162        self.ordinary.get_here(name)
163    }
164
165    /// Binds a tag, and gives back what it was bound to in the same scope.
166    pub fn declare_tag(&mut self, name: Symbol, tag: Tag) -> Option<Tag> {
167        self.tags.declare(name, tag)
168    }
169
170    /// What tag a name names here.
171    #[must_use]
172    pub fn tag(&self, name: Symbol) -> Option<Tag> {
173        self.tags.get(name)
174    }
175
176    /// What tag a name names in the innermost scope alone.
177    ///
178    /// This is the question `struct S;` asks, since a bare declaration of a tag declares a new
179    /// type in this scope even where an outer one is visible, and `struct S *p;` asks the other
180    /// one, since it refers to whatever `S` already means.
181    #[must_use]
182    pub fn tag_here(&self, name: Symbol) -> Option<Tag> {
183        self.tags.get_here(name)
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use rucc_base::Idx;
190    use rucc_types::{IntKind, Types};
191
192    use super::*;
193
194    const S: Symbol = Symbol::from_raw(1);
195
196    #[test]
197    fn a_tag_and_an_ordinary_name_do_not_disturb_each_other() {
198        let types = Types::new();
199        let int = types.int(IntKind::Int);
200        let mut scopes = Scopes::new();
201
202        scopes.declare(S, Binding::Typedef(int));
203        scopes.declare_tag(S, Tag { kind: TagKind::Struct, ty: int });
204
205        assert_eq!(scopes.lookup(S), Some(Binding::Typedef(int)));
206        assert_eq!(scopes.tag(S).map(|tag| tag.kind), Some(TagKind::Struct));
207    }
208
209    #[test]
210    fn an_inner_declaration_hides_an_outer_one_until_its_scope_closes() {
211        let outer = Binding::Decl(Idx::from_usize(0));
212        let inner = Binding::Decl(Idx::from_usize(1));
213        let mut scopes = Scopes::new();
214
215        scopes.declare(S, outer);
216        scopes.push();
217        assert_eq!(scopes.declare(S, inner), None);
218        assert_eq!(scopes.lookup(S), Some(inner));
219        // Which is what makes a use resolve to a declaration rather than to a name.
220        scopes.pop();
221        assert_eq!(scopes.lookup(S), Some(outer));
222    }
223
224    #[test]
225    fn a_tag_declared_again_in_an_inner_scope_is_a_new_type() {
226        let types = Types::new();
227        let int = types.int(IntKind::Int);
228        let long = types.int(IntKind::Long);
229        let mut scopes = Scopes::new();
230
231        scopes.declare_tag(S, Tag { kind: TagKind::Struct, ty: int });
232        scopes.push();
233        // `struct S;` asks what is bound here and finds nothing, so it declares a new type.
234        assert_eq!(scopes.tag_here(S), None);
235        scopes.declare_tag(S, Tag { kind: TagKind::Struct, ty: long });
236        assert_eq!(scopes.tag(S).map(|tag| tag.ty), Some(long));
237        scopes.pop();
238        assert_eq!(scopes.tag(S).map(|tag| tag.ty), Some(int));
239    }
240
241    #[test]
242    fn a_redeclaration_in_one_scope_says_what_it_was() {
243        let first = Binding::Decl(Idx::from_usize(0));
244        let second = Binding::Decl(Idx::from_usize(1));
245        let mut scopes = Scopes::new();
246
247        assert_eq!(scopes.declare(S, first), None);
248        assert_eq!(scopes.declare(S, second), Some(first));
249    }
250}