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 /// What an ordinary identifier names here.
138 #[must_use]
139 pub fn lookup(&self, name: Symbol) -> Option<Binding> {
140 self.ordinary.get(name)
141 }
142
143 /// What an ordinary identifier names in the innermost scope alone.
144 #[must_use]
145 pub fn lookup_here(&self, name: Symbol) -> Option<Binding> {
146 self.ordinary.get_here(name)
147 }
148
149 /// Binds a tag, and gives back what it was bound to in the same scope.
150 pub fn declare_tag(&mut self, name: Symbol, tag: Tag) -> Option<Tag> {
151 self.tags.declare(name, tag)
152 }
153
154 /// What tag a name names here.
155 #[must_use]
156 pub fn tag(&self, name: Symbol) -> Option<Tag> {
157 self.tags.get(name)
158 }
159
160 /// What tag a name names in the innermost scope alone.
161 ///
162 /// This is the question `struct S;` asks, since a bare declaration of a tag declares a new
163 /// type in this scope even where an outer one is visible, and `struct S *p;` asks the other
164 /// one, since it refers to whatever `S` already means.
165 #[must_use]
166 pub fn tag_here(&self, name: Symbol) -> Option<Tag> {
167 self.tags.get_here(name)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use rucc_base::Idx;
174 use rucc_types::{IntKind, Types};
175
176 use super::*;
177
178 const S: Symbol = Symbol::from_raw(1);
179
180 #[test]
181 fn a_tag_and_an_ordinary_name_do_not_disturb_each_other() {
182 let types = Types::new();
183 let int = types.int(IntKind::Int);
184 let mut scopes = Scopes::new();
185
186 scopes.declare(S, Binding::Typedef(int));
187 scopes.declare_tag(S, Tag { kind: TagKind::Struct, ty: int });
188
189 assert_eq!(scopes.lookup(S), Some(Binding::Typedef(int)));
190 assert_eq!(scopes.tag(S).map(|tag| tag.kind), Some(TagKind::Struct));
191 }
192
193 #[test]
194 fn an_inner_declaration_hides_an_outer_one_until_its_scope_closes() {
195 let outer = Binding::Decl(Idx::from_usize(0));
196 let inner = Binding::Decl(Idx::from_usize(1));
197 let mut scopes = Scopes::new();
198
199 scopes.declare(S, outer);
200 scopes.push();
201 assert_eq!(scopes.declare(S, inner), None);
202 assert_eq!(scopes.lookup(S), Some(inner));
203 // Which is what makes a use resolve to a declaration rather than to a name.
204 scopes.pop();
205 assert_eq!(scopes.lookup(S), Some(outer));
206 }
207
208 #[test]
209 fn a_tag_declared_again_in_an_inner_scope_is_a_new_type() {
210 let types = Types::new();
211 let int = types.int(IntKind::Int);
212 let long = types.int(IntKind::Long);
213 let mut scopes = Scopes::new();
214
215 scopes.declare_tag(S, Tag { kind: TagKind::Struct, ty: int });
216 scopes.push();
217 // `struct S;` asks what is bound here and finds nothing, so it declares a new type.
218 assert_eq!(scopes.tag_here(S), None);
219 scopes.declare_tag(S, Tag { kind: TagKind::Struct, ty: long });
220 assert_eq!(scopes.tag(S).map(|tag| tag.ty), Some(long));
221 scopes.pop();
222 assert_eq!(scopes.tag(S).map(|tag| tag.ty), Some(int));
223 }
224
225 #[test]
226 fn a_redeclaration_in_one_scope_says_what_it_was() {
227 let first = Binding::Decl(Idx::from_usize(0));
228 let second = Binding::Decl(Idx::from_usize(1));
229 let mut scopes = Scopes::new();
230
231 assert_eq!(scopes.declare(S, first), None);
232 assert_eq!(scopes.declare(S, second), Some(first));
233 }
234}