1use dashmap::DashMap;
3use parking_lot::RwLock;
4use std::collections::HashSet;
5use std::fmt;
6use std::sync::atomic::Ordering;
7
8use crate::interner::{InternPool, InternedStr};
9use crate::ir::{Arena, HirId};
10use crate::symbol::{NEXT_SCOPE_ID, ScopeId, SymId, SymKindSet, Symbol};
11
12pub struct Scope<'tcx> {
14 id: ScopeId,
16 symbols: DashMap<InternedStr, Vec<&'tcx Symbol>>,
19 owner: HirId,
21 symbol: RwLock<Option<&'tcx Symbol>>,
23 parents: RwLock<Vec<&'tcx Scope<'tcx>>>,
25 #[allow(dead_code)]
27 children: RwLock<Vec<&'tcx Scope<'tcx>>>,
28 redirect: RwLock<Option<ScopeId>>,
31 interner: Option<&'tcx InternPool>,
33}
34
35impl<'tcx> Scope<'tcx> {
36 pub fn new(owner: HirId) -> Self {
38 Self::new_with(owner, None, None)
39 }
40
41 pub fn new_with(
43 owner: HirId,
44 symbol: Option<&'tcx Symbol>,
45 interner: Option<&'tcx InternPool>,
46 ) -> Self {
47 Self {
48 id: ScopeId(NEXT_SCOPE_ID.fetch_add(1, Ordering::Relaxed)),
49 symbols: DashMap::new(),
50 owner,
51 symbol: RwLock::new(symbol),
52 parents: RwLock::new(Vec::new()),
53 children: RwLock::new(Vec::new()),
54 redirect: RwLock::new(None),
55 interner,
56 }
57 }
58
59 pub fn new_with_shards(
62 owner: HirId,
63 symbol: Option<&'tcx Symbol>,
64 interner: Option<&'tcx InternPool>,
65 shard_count: usize,
66 ) -> Self {
67 Self {
68 id: ScopeId(NEXT_SCOPE_ID.fetch_add(1, Ordering::Relaxed)),
69 symbols: DashMap::with_hasher_and_shard_amount(
70 std::hash::RandomState::new(),
71 shard_count,
72 ),
73 owner,
74 symbol: RwLock::new(symbol),
75 parents: RwLock::new(Vec::new()),
76 children: RwLock::new(Vec::new()),
77 redirect: RwLock::new(None),
78 interner,
79 }
80 }
81
82 #[inline]
84 pub fn merge_with(&self, other: &'tcx Scope<'tcx>, _arena: &'tcx Arena<'tcx>) {
85 tracing::trace!(
86 "merge: from scope {:?} to {:?}, {} symbol entries",
87 other.id(),
88 self.id(),
89 other.symbols.len()
90 );
91
92 for entry in other.symbols.iter() {
93 let name_key = *entry.key();
94 let symbol_vec = entry.value().clone();
95 self.symbols.entry(name_key).or_default().extend(symbol_vec);
96 }
97 }
98
99 #[inline]
100 pub fn add_parent(&self, parent: &'tcx Scope<'tcx>) {
101 self.parents.write().push(parent);
102 }
103
104 #[inline]
105 pub fn owner(&self) -> HirId {
106 self.owner
107 }
108
109 #[inline]
110 pub fn set_symbol(&self, symbol: &'tcx Symbol) {
111 *self.symbol.write() = Some(symbol);
112 }
113
114 #[inline]
115 pub fn opt_symbol(&self) -> Option<&'tcx Symbol> {
116 *self.symbol.read()
117 }
118
119 pub fn find_parent_by_kind(&self, kind: crate::symbol::SymKind) -> Option<&'tcx Symbol> {
122 use std::collections::VecDeque;
123 let mut queue = VecDeque::new();
124 queue.extend(self.parents());
125
126 while let Some(parent) = queue.pop_front() {
127 if let Some(sym) = parent.opt_symbol()
128 && sym.kind() == kind
129 {
130 return Some(sym);
131 }
132 queue.extend(parent.parents());
133 }
134 None
135 }
136
137 #[inline]
138 pub fn id(&self) -> ScopeId {
139 self.id
140 }
141
142 pub fn get_redirect(&self) -> Option<ScopeId> {
144 *self.redirect.read()
145 }
146
147 pub fn set_redirect(&self, target: ScopeId) {
149 *self.redirect.write() = Some(target);
150 }
151
152 #[inline]
153 pub fn parents(&self) -> Vec<&'tcx Scope<'tcx>> {
154 self.parents.read().clone()
155 }
156
157 pub fn for_each_symbol<F>(&self, mut visit: F)
160 where
161 F: FnMut(&'tcx Symbol),
162 {
163 let mut keys: Vec<_> = self.symbols.iter().map(|e| *e.key()).collect();
165 keys.sort();
166 for key in keys {
167 if let Some(symbol_vec) = self.symbols.get(&key) {
168 for symbol in symbol_vec.iter() {
169 visit(symbol);
170 }
171 }
172 }
173 }
174
175 pub fn insert(&self, symbol: &'tcx Symbol) -> SymId {
177 self.symbols.entry(symbol.name).or_default().push(symbol);
178 symbol.id
179 }
180
181 pub fn lookup_symbols(
182 &self,
183 name: InternedStr,
184 options: LookupOptions,
185 ) -> Option<Vec<&'tcx Symbol>> {
186 let guard = self.symbols.get(&name)?;
187 let symbols = guard.value();
188
189 if options.kind_filters.is_empty() && options.unit_filters.is_none() {
191 return Some(symbols.clone());
192 }
193
194 let filtered: Vec<&'tcx Symbol> = symbols
196 .iter()
197 .filter(|symbol| {
198 if !options.kind_filters.is_empty() && !options.kind_filters.contains(symbol.kind())
200 {
201 return false;
202 }
203 if let Some(unit) = options.unit_filters
204 && symbol.unit_index() != Some(unit)
205 {
206 return false;
207 }
208 true
209 })
210 .copied()
211 .collect();
212
213 if filtered.is_empty() {
214 None
215 } else {
216 Some(filtered)
217 }
218 }
219}
220
221impl<'tcx> fmt::Debug for ScopeStack<'tcx> {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 let stack = self.stack.read();
224 let depth = stack.len();
225
226 let scopes_debug: Vec<_> = stack
228 .iter()
229 .map(|scope| {
230 let mut symbol_entries: Vec<String> = Vec::new();
231
232 scope.for_each_symbol(|s| {
233 symbol_entries.push(s.format(Some(self.interner)));
234 });
235
236 (scope.id(), scope.owner, symbol_entries)
237 })
238 .collect();
239
240 f.debug_struct("ScopeStack")
241 .field("depth", &depth)
242 .field("scopes", &scopes_debug)
243 .finish()
244 }
245}
246
247impl<'tcx> fmt::Debug for Scope<'tcx> {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 let symbol_desc = self.opt_symbol().cloned();
250 let mut symbol_entries: Vec<String> = Vec::new();
251
252 self.for_each_symbol(|s| {
253 symbol_entries.push(s.format(self.interner));
254 });
255
256 f.debug_struct("Scope")
257 .field("id", &self.id)
258 .field("owner", &self.owner)
259 .field("symbol", &symbol_desc)
260 .field("symbols", &symbol_entries)
261 .finish()
262 }
263}
264
265pub struct ScopeStack<'tcx> {
267 arena: &'tcx Arena<'tcx>,
268 interner: &'tcx InternPool,
269 stack: RwLock<Vec<&'tcx Scope<'tcx>>>,
271}
272
273impl<'tcx> Clone for ScopeStack<'tcx> {
274 fn clone(&self) -> Self {
275 Self {
276 arena: self.arena,
277 interner: self.interner,
278 stack: RwLock::new(self.stack.read().clone()),
279 }
280 }
281}
282
283impl<'tcx> ScopeStack<'tcx> {
284 pub fn new(arena: &'tcx Arena<'tcx>, interner: &'tcx InternPool) -> Self {
285 Self {
286 arena,
287 interner,
288 stack: RwLock::new(Vec::new()),
289 }
290 }
291
292 #[inline]
293 pub fn depth(&self) -> usize {
294 self.stack.read().len()
295 }
296
297 #[inline]
298 pub fn push(&self, scope: &'tcx Scope<'tcx>) {
299 self.stack.write().push(scope);
300 }
301
302 #[inline]
303 pub fn globals(&self) -> &'tcx Scope<'tcx> {
304 self.stack.read().first().copied().unwrap()
305 }
306
307 #[inline]
308 pub fn top(&self) -> Option<&'tcx Scope<'tcx>> {
309 self.stack.read().last().copied()
310 }
311
312 pub fn push_recursive(&self, scope: &'tcx Scope<'tcx>) {
314 let mut candidates = vec![scope];
315 let mut linear_chain = Vec::new();
316 let mut visited = HashSet::new();
317
318 while let Some(current) = candidates.pop() {
320 if !visited.insert(current.id()) {
321 continue;
322 }
323 linear_chain.push(current);
324
325 let parents = current.parents.read();
326 for base in parents.iter().rev() {
328 if !visited.contains(&base.id()) {
329 candidates.push(base);
330 }
331 }
332 }
333
334 let mut stack = self.stack.write();
336 for s in linear_chain.iter().rev() {
337 stack.push(s);
338 }
339 }
340
341 #[inline]
342 pub fn pop(&self) -> Option<&'tcx Scope<'tcx>> {
343 self.stack.write().pop()
344 }
345
346 pub fn pop_until(&self, depth: usize) {
347 let mut stack = self.stack.write();
348 while stack.len() > depth {
349 stack.pop();
350 }
351 }
352
353 pub fn lookup_symbols(&self, name: &str, options: LookupOptions) -> Option<Vec<&'tcx Symbol>> {
356 if name.is_empty() {
357 return None;
358 }
359 let name_key = self.interner.intern(name);
360 let stack = self.stack.read();
361 tracing::trace!("stack: {:#?}", stack);
362
363 let symbols = stack.iter().rev().find_map(|scope| {
364 let result = scope.lookup_symbols(name_key, options);
365 if let Some(ref syms) = result {
366 tracing::trace!(
367 "found '{}' in scope {:?}, symbols: {:?}",
368 name,
369 scope.id(),
370 syms.iter()
371 .map(|s| (s.id(), s.unit_index()))
372 .collect::<Vec<_>>()
373 );
374 }
375 result
376 });
377 tracing::trace!(
378 "lookup_symbols: '{}' found {:?} in scope stack",
379 name,
380 symbols.as_ref().map(|syms| syms
381 .iter()
382 .map(|s| s.format(Some(self.interner)))
383 .collect::<Vec<_>>())
384 );
385 symbols
386 }
387
388 fn normalize_name(&self, name: &str, force: bool) -> Option<InternedStr> {
390 let name_to_intern = if !name.is_empty() {
391 name
392 } else if force {
393 "___llmcc_anonymous___"
394 } else {
395 return None;
396 };
397 Some(self.interner.intern(name_to_intern))
398 }
399
400 pub fn lookup_or_insert(
403 &self,
404 name: &str,
405 node: HirId,
406 options: LookupOptions,
407 ) -> Option<Vec<&'tcx Symbol>> {
408 let name_key = self.normalize_name(name, options.force)?;
409
410 let stack = self.stack.read();
411 if stack.is_empty() {
412 return None;
413 }
414
415 let scope = if options.global {
416 tracing::trace!("lookup_or_insert: '{}' in global scope", name);
417 stack.first().copied()?
418 } else if options.parent && stack.len() >= 2 {
419 tracing::trace!("lookup_or_insert: '{}' in parent scope", name);
420 stack.get(stack.len() - 2).copied()?
421 } else {
422 tracing::trace!("lookup_or_insert: '{}' in current scope", name);
423 stack.last().copied()?
424 };
425
426 let lookup_options = if !options.kind_filters.is_empty() {
428 LookupOptions::default().with_kind_set(options.kind_filters)
429 } else {
430 LookupOptions::default()
431 };
432 let symbols = scope.lookup_symbols(name_key, lookup_options);
433 if let Some(mut symbols) = symbols {
434 debug_assert!(!symbols.is_empty());
435
436 if options.chained {
437 tracing::debug!("chained symbol '{}' in scope {:?}", name, scope.id());
438 let symbol = symbols.last().copied().unwrap();
440 let new_symbol = Symbol::new(node, name_key);
441 new_symbol.set_previous(symbol.id);
442 let sym_id = new_symbol.id().0;
443 let allocated = self.arena.alloc_with_id(sym_id, new_symbol);
444 scope.insert(allocated);
445 symbols.push(allocated);
446 Some(symbols)
447 } else {
448 tracing::trace!("found existing symbol '{}' in scope {:?}", name, scope.id());
449 Some(symbols)
450 }
451 } else {
452 tracing::trace!("create new symbol '{}' in scope {:?}", name, scope.id());
453 let new_symbol = Symbol::new(node, name_key);
455 let sym_id = new_symbol.id().0;
456 let allocated = self.arena.alloc_with_id(sym_id, new_symbol);
457 scope.insert(allocated);
458 Some(vec![allocated])
459 }
460 }
461
462 pub fn lookup_qualified(
465 &self,
466 qualified_name: &[&str],
467 options: LookupOptions,
468 ) -> Option<Vec<&'tcx Symbol>> {
469 if qualified_name.is_empty() {
470 return None;
471 }
472
473 let stack = self.stack.read();
474 if stack.is_empty() {
475 return None;
476 }
477
478 let name_key = self.interner.intern(qualified_name[0]);
480 let mut current_scope = *stack.first()?;
481 if options.shift_start {
482 for i in 0..stack.len() {
483 if stack[i].lookup_symbols(name_key, options).is_some() {
484 current_scope = stack[i];
485 break;
486 }
487 }
488 tracing::trace!(
489 "lookup_qualified: shifted start to scope {:?} for '{}'",
490 current_scope.id(),
491 qualified_name[0]
492 );
493 }
494
495 if current_scope.lookup_symbols(name_key, options).is_none() {
496 tracing::trace!(
497 "lookup_qualified: starting scope {:?} does not contain '{}' options: {:?}, stack {:#?}",
498 current_scope.id(),
499 qualified_name[0],
500 options,
501 stack
502 );
503 return None;
504 }
505
506 self.lookup_qualified_recursive(current_scope, qualified_name, 0, &options)
508 }
509
510 fn lookup_qualified_recursive(
512 &self,
513 scope: &'tcx Scope<'tcx>,
514 qualified_name: &[&str],
515 index: usize,
516 options: &LookupOptions,
517 ) -> Option<Vec<&'tcx Symbol>> {
518 if index >= qualified_name.len() {
519 return None;
520 }
521
522 tracing::trace!(
523 "lookup_qualified_recursive: scope {:?}, part '{}'",
524 scope.id(),
525 qualified_name[index]
526 );
527 let part = qualified_name[index];
528 let name_key = self.interner.intern(part);
529 let symbols = scope.lookup_symbols(name_key, *options)?;
530 tracing::trace!(
531 "lookup_qualified_recursive: found {:?} symbols for '{}' in scope {:?}",
532 symbols
533 .iter()
534 .map(|s| s.format(Some(self.interner)))
535 .collect::<Vec<_>>(),
536 part,
537 scope.id()
538 );
539
540 if index == qualified_name.len() - 1 {
542 return Some(symbols);
543 }
544
545 let mut results = Vec::new();
547 for symbol in symbols {
548 if let Some(symbol_scope_id) = symbol.opt_scope() {
549 if let Some(next_scope) = self.arena.get_scope(symbol_scope_id.0) {
551 debug_assert!(next_scope.id() == symbol_scope_id);
552 tracing::trace!(
553 "lookup_qualified_recursive: descending into scope {:?} for symbol '{}'",
554 next_scope.id(),
555 symbol.format(Some(self.interner))
556 );
557 if let Some(result) = self.lookup_qualified_recursive(
559 next_scope,
560 qualified_name,
561 index + 1,
562 options,
563 ) {
564 results.extend(result);
565 }
566 }
567 }
568 }
569
570 if results.is_empty() {
571 None
572 } else {
573 Some(results)
574 }
575 }
576}
577
578#[derive(Debug, Clone, Copy, Default)]
579pub struct LookupOptions {
580 pub global: bool,
581 pub parent: bool,
582 pub chained: bool,
583 pub force: bool,
584 pub shift_start: bool,
585 pub kind_filters: SymKindSet,
586 pub unit_filters: Option<usize>,
587}
588
589impl LookupOptions {
590 pub fn current() -> Self {
591 Self::default()
592 }
593
594 pub fn global() -> Self {
595 Self {
596 global: true,
597 ..Default::default()
598 }
599 }
600
601 pub fn parent() -> Self {
602 Self {
603 parent: true,
604 ..Default::default()
605 }
606 }
607
608 pub fn chained() -> Self {
609 Self {
610 chained: true,
611 ..Default::default()
612 }
613 }
614
615 pub fn anonymous() -> Self {
616 Self {
617 force: true,
618 ..Default::default()
619 }
620 }
621
622 pub fn with_global(mut self, global: bool) -> Self {
623 self.global = global;
624 self
625 }
626
627 pub fn with_parent(mut self, parent: bool) -> Self {
628 self.parent = parent;
629 self
630 }
631
632 pub fn with_chained(mut self, chained: bool) -> Self {
633 self.chained = chained;
634 self
635 }
636
637 pub fn with_force(mut self, force: bool) -> Self {
638 self.force = force;
639 self
640 }
641
642 pub fn with_shift_start(mut self, shift: bool) -> Self {
643 self.shift_start = shift;
644 self
645 }
646
647 pub fn with_kind_set(mut self, kind_set: SymKindSet) -> Self {
648 self.kind_filters = kind_set;
649 self
650 }
651
652 pub fn with_unit_filter(mut self, unit: usize) -> Self {
653 self.unit_filters = Some(unit);
654 self
655 }
656}