1use std::cell::{Cell, RefCell};
2use std::collections::HashMap;
3
4use crate::graph_builder::BlockId;
5use crate::interner::{InternPool, InternedStr};
6use crate::ir::{Arena, HirId, HirIdent};
7use crate::trie::SymbolTrie;
8use std::sync::atomic::{AtomicU32, Ordering};
9
10static NEXT_SYMBOL_ID: AtomicU32 = AtomicU32::new(1);
11
12#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
13pub struct SymId(pub u32);
14
15impl std::fmt::Display for SymId {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(f, "{}", self.0)
18 }
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum SymbolKind {
23 Unknown,
24 Module,
25 Struct,
26 Enum,
27 Function,
28 Variable,
29 Const,
30 Static,
31 Trait,
32 Impl,
33 EnumVariant,
34}
35
36#[derive(Debug)]
38pub struct Scope<'tcx> {
39 trie: RefCell<SymbolTrie<'tcx>>,
41 owner: HirId,
43 symbol: Cell<Option<&'tcx Symbol>>,
45}
46
47impl<'tcx> Scope<'tcx> {
48 pub fn new(owner: HirId) -> Self {
49 Self {
50 trie: RefCell::new(SymbolTrie::default()),
51 owner,
52 symbol: Cell::new(None),
53 }
54 }
55
56 pub fn owner(&self) -> HirId {
57 self.owner
58 }
59
60 pub fn symbol(&self) -> Option<&'tcx Symbol> {
61 self.symbol.get()
62 }
63
64 pub fn set_symbol(&self, symbol: Option<&'tcx Symbol>) {
65 self.symbol.set(symbol);
66 }
67
68 pub fn insert(&self, symbol: &'tcx Symbol, interner: &InternPool) -> SymId {
69 let sym_id = symbol.id;
70 self.trie.borrow_mut().insert_symbol(symbol, interner);
71 sym_id
72 }
73
74 pub fn get_id(&self, key: InternedStr) -> Option<SymId> {
75 let hits = self.trie.borrow().lookup_symbol_suffix(&[key]);
76 hits.first().map(|symbol| symbol.id)
77 }
78
79 pub fn lookup_suffix_once(&self, suffix: &[InternedStr]) -> Option<&'tcx Symbol> {
80 self.trie
81 .borrow()
82 .lookup_symbol_suffix(suffix)
83 .into_iter()
84 .next()
85 }
86
87 pub fn lookup_suffix_symbols(&self, suffix: &[InternedStr]) -> Vec<&'tcx Symbol> {
88 self.trie.borrow().lookup_symbol_suffix(suffix)
89 }
90
91 pub fn format_compact(&self) -> String {
92 let count = self.trie.borrow().total_symbols();
93 format!("{}/{}", self.owner, count)
94 }
95
96 pub fn all_symbols(&self) -> Vec<&'tcx Symbol> {
97 self.trie.borrow().symbols()
98 }
99}
100
101#[derive(Debug)]
102pub struct ScopeStack<'tcx> {
103 arena: &'tcx Arena<'tcx>,
104 interner: &'tcx InternPool,
105 stack: Vec<&'tcx Scope<'tcx>>,
106 symbol_map: &'tcx RefCell<HashMap<SymId, &'tcx Symbol>>,
107}
108
109impl<'tcx> ScopeStack<'tcx> {
110 pub fn new(
111 arena: &'tcx Arena<'tcx>,
112 interner: &'tcx InternPool,
113 symbol_map: &'tcx RefCell<HashMap<SymId, &'tcx Symbol>>,
114 ) -> Self {
115 Self {
116 arena,
117 interner,
118 stack: Vec::new(),
119 symbol_map,
120 }
121 }
122
123 pub fn depth(&self) -> usize {
124 self.stack.len()
125 }
126
127 pub fn push(&mut self, scope: &'tcx Scope<'tcx>) {
128 self.push_with_symbol(scope, None);
129 }
130
131 pub fn push_with_symbol(&mut self, scope: &'tcx Scope<'tcx>, symbol: Option<&'tcx Symbol>) {
132 scope.set_symbol(symbol);
133 self.stack.push(scope);
134 }
135
136 pub fn pop(&mut self) -> Option<&'tcx Scope<'tcx>> {
137 self.stack.pop()
138 }
139
140 pub fn pop_until(&mut self, depth: usize) {
141 while self.depth() > depth {
142 self.pop();
143 }
144 }
145
146 pub fn top(&self) -> Option<&'tcx Scope<'tcx>> {
147 self.stack.last().copied()
148 }
149
150 pub fn scoped_symbol(&self) -> Option<&'tcx Symbol> {
151 self.stack.iter().rev().find_map(|scope| scope.symbol())
152 }
153
154 pub fn iter(&self) -> impl DoubleEndedIterator<Item = &'tcx Scope<'tcx>> + '_ {
155 self.stack.iter().copied()
156 }
157
158 pub fn lookup_scoped_suffix_once(&self, suffix: &[InternedStr]) -> Option<&'tcx Symbol> {
159 self.find_scoped_suffix_with_filters(suffix, None, None)
160 }
161
162 pub fn find_scoped_suffix_with_filters(
163 &self,
164 suffix: &[InternedStr],
165 kind: Option<SymbolKind>,
166 file: Option<usize>,
167 ) -> Option<&'tcx Symbol> {
168 for scope in self.iter().rev() {
169 let symbols = scope.trie.borrow().lookup_symbol_suffix(suffix);
170 if let Some(symbol) = select_symbol(symbols, kind, file) {
171 return Some(symbol);
172 }
173 }
174 None
175 }
176
177 fn scope_for_insertion(&mut self, global: bool) -> Result<&'tcx Scope<'tcx>, &'static str> {
178 if global {
179 self.stack.first().copied().ok_or("no global scope exists")
180 } else {
181 self.stack
182 .last()
183 .copied()
184 .ok_or("no active scope available")
185 }
186 }
187
188 pub fn insert_symbol(
189 &mut self,
190 symbol: &'tcx Symbol,
191 global: bool,
192 ) -> Result<SymId, &'static str> {
193 let scope = self.scope_for_insertion(global)?;
194 Ok(scope.insert(symbol, self.interner))
195 }
196
197 pub fn find_symbol_id(&self, name: &str) -> Option<SymId> {
198 let key = self.interner.intern(name);
199 self.iter().rev().find_map(|scope| scope.get_id(key))
200 }
201
202 fn find_symbol_local_by_key(&self, key: InternedStr) -> Option<&'tcx Symbol> {
203 let scopes = if self.stack.len() > 1 {
204 &self.stack[1..]
205 } else {
206 &self.stack[..]
207 };
208
209 scopes.iter().rev().find_map(|scope| {
210 scope
211 .trie
212 .borrow()
213 .lookup_symbol_suffix(&[key])
214 .into_iter()
215 .next()
216 })
217 }
218
219 pub fn find_symbol_local(&self, name: &str) -> Option<&'tcx Symbol> {
220 let key = self.interner.intern(name);
221 self.find_symbol_local_by_key(key)
222 }
223
224 pub fn find_global_suffix_vec(&self, suffix: &[InternedStr]) -> Vec<&'tcx Symbol> {
225 self.stack
226 .first()
227 .map(|scope| scope.trie.borrow().lookup_symbol_suffix(suffix))
228 .unwrap_or_default()
229 }
230
231 pub fn find_global_suffix(&self, suffix: &[InternedStr]) -> Option<&'tcx Symbol> {
232 self.find_global_suffix_with_filters(suffix, None, None)
233 }
234
235 pub fn find_global_suffix_with_filters(
236 &self,
237 suffix: &[InternedStr],
238 kind: Option<SymbolKind>,
239 file: Option<usize>,
240 ) -> Option<&'tcx Symbol> {
241 let symbols = self.find_global_suffix_vec(suffix);
242 select_symbol(symbols, kind, file)
243 }
244
245 pub fn find_global_suffix_in_unit(
247 &self,
248 suffix: &[InternedStr],
249 unit_index: usize,
250 ) -> Option<&'tcx Symbol> {
251 self.find_global_suffix_with_filters(suffix, None, Some(unit_index))
252 }
253
254 pub fn insert_with<F>(
255 &mut self,
256 owner: HirId,
257 ident: &HirIdent<'tcx>,
258 global: bool,
259 init: F,
260 ) -> &'tcx Symbol
261 where
262 F: FnOnce(&'tcx Symbol),
263 {
264 let key = self.interner.intern(&ident.name);
265
266 let symbol = self.alloc_symbol(owner, ident, key);
267 init(symbol);
268
269 self.insert_symbol(symbol, false)
270 .expect("failed to insert symbol into scope");
271 if global {
272 self.insert_symbol(symbol, true)
273 .expect("failed to insert symbol into global scope");
274 }
275
276 symbol
277 }
278
279 fn alloc_symbol(&self, owner: HirId, ident: &HirIdent<'tcx>, key: InternedStr) -> &'tcx Symbol {
280 let symbol = Symbol::new(owner, ident.name.clone(), key);
281 let symbol = self.arena.alloc(symbol);
282 self.symbol_map.borrow_mut().insert(symbol.id, symbol);
283 symbol
284 }
285}
286
287#[derive(Debug, Clone)]
289pub struct Symbol {
290 pub id: SymId,
292 pub owner: Cell<HirId>,
294 pub name: String,
296 pub name_key: InternedStr,
298 pub fqn_name: RefCell<String>,
300 pub fqn_key: RefCell<InternedStr>,
302 pub depends: RefCell<Vec<SymId>>,
307 pub depended: RefCell<Vec<SymId>>,
308 pub kind: Cell<SymbolKind>,
309 pub unit_index: Cell<Option<usize>>,
311 pub block_id: Cell<Option<BlockId>>,
313}
314
315impl Symbol {
316 pub fn new(owner: HirId, name: String, name_key: InternedStr) -> Self {
317 let id = NEXT_SYMBOL_ID.fetch_add(1, Ordering::SeqCst);
318 let sym_id = SymId(id);
319
320 let fqn_key = name_key;
321
322 Self {
323 id: sym_id,
324 owner: Cell::new(owner),
325 name: name.clone(),
326 name_key,
327 fqn_name: RefCell::new(name),
328 fqn_key: RefCell::new(fqn_key),
329 depends: RefCell::new(Vec::new()),
330 depended: RefCell::new(Vec::new()),
331 kind: Cell::new(SymbolKind::Unknown),
332 unit_index: Cell::new(None),
333 block_id: Cell::new(None),
334 }
335 }
336
337 pub fn owner(&self) -> HirId {
338 self.owner.get()
339 }
340
341 pub fn set_owner(&self, owner: HirId) {
342 self.owner.set(owner);
343 }
344
345 pub fn format_compact(&self) -> String {
346 format!("{}->{} \"{}\"", self.id, self.owner.get(), self.name)
347 }
348
349 pub fn set_fqn(&self, fqn: String, interner: &InternPool) {
350 let key = interner.intern(&fqn);
351 *self.fqn_name.borrow_mut() = fqn;
352 *self.fqn_key.borrow_mut() = key;
353 }
354
355 pub fn kind(&self) -> SymbolKind {
356 self.kind.get()
357 }
358
359 pub fn set_kind(&self, kind: SymbolKind) {
360 self.kind.set(kind);
361 }
362
363 pub fn unit_index(&self) -> Option<usize> {
364 self.unit_index.get()
365 }
366
367 pub fn set_unit_index(&self, file: usize) {
368 if self.unit_index.get().is_none() {
369 self.unit_index.set(Some(file));
370 }
371 }
372
373 pub fn add_depends_on(&self, sym_id: SymId) {
374 if sym_id == self.id {
375 return;
376 }
377 let mut deps = self.depends.borrow_mut();
378 if deps.contains(&sym_id) {
379 return;
380 }
381 deps.push(sym_id);
382 }
383
384 pub fn add_depended_by(&self, sym_id: SymId) {
385 if sym_id == self.id {
386 return;
387 }
388 let mut deps = self.depended.borrow_mut();
389 if deps.contains(&sym_id) {
390 return;
391 }
392 deps.push(sym_id);
393 }
394
395 pub fn add_dependency(&self, other: &Symbol) {
396 self.add_depends_on(other.id);
397 other.add_depended_by(self.id);
398 }
399
400 pub fn block_id(&self) -> Option<BlockId> {
401 self.block_id.get()
402 }
403
404 pub fn set_block_id(&self, block_id: Option<BlockId>) {
405 self.block_id.set(block_id);
406 }
407}
408
409fn select_symbol(
410 candidates: Vec<&Symbol>,
411 kind: Option<SymbolKind>,
412 file: Option<usize>,
413) -> Option<&Symbol> {
414 if candidates.is_empty() {
415 return None;
416 }
417
418 if let Some(kind) = kind {
419 let matches: Vec<&Symbol> = candidates
420 .iter()
421 .copied()
422 .filter(|symbol| symbol.kind() == kind)
423 .collect();
424
425 if let Some(file) = file {
426 if let Some(symbol) = matches
427 .iter()
428 .copied()
429 .find(|symbol| symbol.unit_index() == Some(file))
430 {
431 return Some(symbol);
432 }
433 }
434
435 if !matches.is_empty() {
436 return Some(matches[0]);
437 }
438 }
439
440 if let Some(file) = file {
441 if let Some(symbol) = candidates
442 .iter()
443 .copied()
444 .find(|candidate| candidate.unit_index() == Some(file))
445 {
446 return Some(symbol);
447 }
448 }
449
450 candidates.into_iter().next()
451}