1use std::sync::atomic::{AtomicU64, Ordering};
2use std::time::Instant;
3
4use llmcc_core::context::CompileUnit;
5use llmcc_core::interner::InternPool;
6use llmcc_core::ir::HirScope;
7use llmcc_core::scope::{LookupOptions, Scope, ScopeStack};
8use llmcc_core::symbol::{ScopeId, SymKind, SymKindSet, Symbol};
9use llmcc_core::{CompileCtxt, LanguageTraitImpl};
10
11use rayon::prelude::*;
12
13use crate::ResolverOption;
14
15#[derive(Debug)]
16pub struct BinderScopes<'a> {
17 unit: CompileUnit<'a>,
18 scopes: ScopeStack<'a>,
19}
20
21impl<'a> BinderScopes<'a> {
22 pub fn new(unit: CompileUnit<'a>, globals: &'a Scope<'a>) -> Self {
23 let scopes = ScopeStack::new(&unit.cc.arena, &unit.cc.interner);
24 scopes.push(globals);
25
26 Self { unit, scopes }
27 }
28
29 #[inline]
30 pub fn top(&self) -> &'a Scope<'a> {
31 self.scopes.top().unwrap()
32 }
33
34 #[inline]
35 pub fn unit(&self) -> CompileUnit<'a> {
36 self.unit
37 }
38
39 #[inline]
40 pub fn interner(&self) -> &InternPool {
41 self.unit.interner()
42 }
43
44 #[inline]
45 pub fn scopes(&self) -> &ScopeStack<'a> {
46 &self.scopes
47 }
48
49 #[inline]
50 pub fn scopes_mut(&mut self) -> &mut ScopeStack<'a> {
51 &mut self.scopes
52 }
53
54 #[inline]
60 pub fn scope_depth(&self) -> usize {
61 self.scopes.depth()
62 }
63
64 pub fn push_scope(&mut self, id: ScopeId) {
66 tracing::trace!("push_scope: {:?}", id);
67 let scope = self.unit.get_scope(id);
68 self.scopes.push(scope);
69 }
70
71 pub fn push_scope_recursive(&mut self, id: ScopeId) {
73 tracing::trace!("push_scope_recursive: {:?}", id);
74 let scope = self.unit.get_scope(id);
75 self.scopes.push_recursive(scope);
76 }
77
78 pub fn push_scope_node(&mut self, sn: &'a HirScope<'a>) {
80 if sn.opt_ident().is_some() {
81 self.push_scope_recursive(sn.scope().id());
82 } else {
83 self.push_scope(sn.scope().id());
84 }
85 }
86
87 #[inline]
89 pub fn pop_scope(&mut self) {
90 tracing::trace!("pop_scope: depth {}", self.scopes.depth());
91 self.scopes.pop();
92 }
93
94 #[inline]
96 pub fn pop_until(&mut self, depth: usize) {
97 tracing::trace!("pop_until: {} -> {}", self.scopes.depth(), depth);
98 self.scopes.pop_until(depth);
99 }
100
101 #[inline]
103 pub fn globals(&self) -> &'a Scope<'a> {
104 self.scopes.globals()
105 }
106
107 #[inline]
108 pub fn lookup_globals(&self, name: &str, kind_filters: SymKindSet) -> Option<Vec<&'a Symbol>> {
109 tracing::trace!("lookup globals '{}' with filters {:?}", name, kind_filters);
110 let options = LookupOptions::current().with_kind_set(kind_filters);
111 let name_key = self.unit.cc.interner.intern(name);
112 self.scopes.globals().lookup_symbols(name_key, options)
113 }
114
115 #[inline]
116 pub fn lookup_global(&self, name: &str, kind_filters: SymKindSet) -> Option<&'a Symbol> {
117 let symbols = self.lookup_globals(name, kind_filters)?;
118 if symbols.len() > 1 {
119 tracing::warn!(
120 "multiple global symbols found for '{}', returning the last one",
121 name
122 );
123 }
124 symbols.last().copied()
125 }
126
127 #[inline]
129 pub fn lookup_symbols(&self, name: &str, kind_filters: SymKindSet) -> Option<Vec<&'a Symbol>> {
130 tracing::trace!("lookup symbols '{}' with filters {:?}", name, kind_filters);
131 let options = LookupOptions::current().with_kind_set(kind_filters);
132 self.scopes.lookup_symbols(name, options)
133 }
134
135 #[inline]
136 pub fn lookup_symbol(&self, name: &str, kind_filters: SymKindSet) -> Option<&'a Symbol> {
137 let symbols = self.lookup_symbols(name, kind_filters)?;
138 if symbols.len() > 1 {
139 let current_unit = self.unit.index;
140 let current_crate_index = self.unit.unit_meta().crate_index;
141
142 if let Some(local_sym) = symbols
144 .iter()
145 .find(|s| s.unit_index() == Some(current_unit))
146 {
147 tracing::trace!(
148 "lookup_symbol: multiple found for '{}', preferring local symbol {:?}",
149 name,
150 local_sym.id()
151 );
152 return Some(*local_sym);
153 }
154
155 let same_crate_symbols: Vec<_> = symbols
158 .iter()
159 .filter(|s| s.crate_index() == Some(current_crate_index))
160 .copied()
161 .collect();
162
163 if !same_crate_symbols.is_empty() && same_crate_symbols.len() < symbols.len() {
164 tracing::trace!(
166 "lookup_symbol: multiple found for '{}', preferring same-crate symbol {:?}",
167 name,
168 same_crate_symbols.last().map(|s| s.id())
169 );
170 return same_crate_symbols.last().copied();
171 }
172
173 tracing::warn!(
174 "multiple symbols found for '{}', returning the last one",
175 name
176 );
177 }
178 symbols.last().copied()
179 }
180
181 pub fn lookup_member_symbols(
183 &self,
184 obj_type_symbol: &'a Symbol,
185 member_name: &str,
186 kind_filters: SymKindSet,
187 ) -> Option<&'a Symbol> {
188 if !kind_filters.is_empty() {
189 tracing::trace!(
190 "looking up member '{}' in type scope with filters {:?}",
191 member_name,
192 kind_filters
193 );
194 for kind in [
196 SymKind::Method,
197 SymKind::Function,
198 SymKind::Field,
199 SymKind::Variable,
200 SymKind::Const,
201 SymKind::Static,
202 ] {
203 if kind_filters.contains(kind) {
204 tracing::trace!(" filter: {:?}", kind);
205 let sym = self.lookup_member_symbol(obj_type_symbol, member_name, Some(kind));
206 if sym.is_some() {
207 return sym;
208 }
209 }
210 }
211 } else {
212 tracing::trace!("looking up member '{}' in type scope", member_name);
213 }
214 None
215 }
216
217 pub fn lookup_member_symbol(
219 &self,
220 obj_type_symbol: &'a Symbol,
221 member_name: &str,
222 kind_filter: Option<SymKind>,
223 ) -> Option<&'a Symbol> {
224 tracing::trace!(
225 "lookup_member_symbol: '{}' in {:?} (kind={:?})",
226 member_name,
227 obj_type_symbol.name,
228 obj_type_symbol.kind()
229 );
230
231 let effective_symbol = if obj_type_symbol.kind() == SymKind::TypeAlias {
233 if let Some(type_of_id) = obj_type_symbol.type_of() {
234 let resolved = self
235 .unit
236 .cc
237 .opt_get_symbol(type_of_id)
238 .unwrap_or(obj_type_symbol);
239 tracing::trace!(
240 " -> followed type_of to {:?} (kind={:?})",
241 resolved.name,
242 resolved.kind()
243 );
244 resolved
245 } else {
246 obj_type_symbol
247 }
248 } else {
249 obj_type_symbol
250 };
251
252 let scope_id = effective_symbol.opt_scope()?;
253 let scope = self.unit.get_scope(scope_id);
254
255 let scopes = ScopeStack::new(&self.unit.cc.arena, &self.unit.cc.interner);
257 scopes.push_recursive(scope);
258
259 let options = if let Some(filter) = kind_filter {
260 LookupOptions::current().with_kind_set(SymKindSet::from_kind(filter))
261 } else {
262 LookupOptions::current()
263 };
264
265 scopes
266 .lookup_symbols(member_name, options)?
267 .into_iter()
268 .last()
269 }
270
271 pub fn lookup_qualified(
273 &self,
274 qualified_name: &[&str],
275 kind_filters: SymKindSet,
276 ) -> Option<Vec<&'a Symbol>> {
277 tracing::trace!(
278 "lookup qualified {:?} with kind_filters {:?}",
279 qualified_name,
280 kind_filters
281 );
282 let mut options = LookupOptions::default().with_shift_start(true);
283 if !kind_filters.is_empty() {
284 options = options.with_kind_set(kind_filters)
285 }
286 let symbols = self.scopes.lookup_qualified(qualified_name, options)?;
287 Some(symbols)
288 }
289
290 pub fn lookup_qualified_symbol(
292 &self,
293 qualified_name: &[&str],
294 kind_filters: SymKindSet,
295 ) -> Option<&'a Symbol> {
296 let symbols = self.lookup_qualified(qualified_name, kind_filters)?;
297 let current_unit = self.unit.index;
298
299 tracing::trace!(
300 "lookup_qualified_symbol: '{:?}' found {} symbols, current_unit={}: {:?}",
301 qualified_name,
302 symbols.len(),
303 current_unit,
304 symbols
305 .iter()
306 .map(|s| (s.id(), s.unit_index()))
307 .collect::<Vec<_>>()
308 );
309
310 if symbols.len() > 1 {
311 if let Some(local_sym) = symbols
313 .iter()
314 .find(|s| s.unit_index() == Some(current_unit))
315 {
316 tracing::trace!(" -> preferring local symbol {:?}", local_sym.id());
317 return Some(*local_sym);
318 }
319
320 let current_crate_index = self.unit.unit_meta().crate_index;
322 if let Some(same_crate_sym) = symbols
323 .iter()
324 .find(|s| s.crate_index() == Some(current_crate_index))
325 {
326 tracing::trace!(
327 "preferring same-crate symbol for qualified '{:?}' crate_index={}",
328 qualified_name,
329 current_crate_index
330 );
331 return Some(*same_crate_sym);
332 }
333
334 tracing::warn!(
335 "multiple symbols found for qualified '{:?}', returning the last one",
336 qualified_name
337 );
338 }
339 symbols.last().copied()
340 }
341}
342
343pub fn bind_symbols_with<'a, L: LanguageTraitImpl>(
348 cc: &'a CompileCtxt<'a>,
349 globals: &'a Scope<'a>,
350 config: &ResolverOption,
351) {
352 let total_start = Instant::now();
353
354 tracing::info!("starting symbol binding for total {} units", cc.files.len());
355
356 let bind_cpu_time_ns = AtomicU64::new(0);
358
359 let bind_unit = |unit_index: usize| {
360 let bind_start = Instant::now();
361
362 tracing::debug!("binding symbols for unit {}", unit_index);
363 let unit = cc.compile_unit(unit_index);
364 let id = unit.file_root_id().unwrap();
365 let node = unit.hir_node(id);
366 L::bind_symbols(unit, node, globals, config);
367
368 bind_cpu_time_ns.fetch_add(bind_start.elapsed().as_nanos() as u64, Ordering::Relaxed);
369 };
370
371 let parallel_start = Instant::now();
372 if config.sequential {
373 tracing::debug!("running symbol binding sequentially");
374 (0..cc.files.len()).for_each(bind_unit);
375 } else {
376 tracing::debug!("running symbol binding in parallel");
377 (0..cc.files.len()).into_par_iter().for_each(bind_unit);
378 }
379 let parallel_time = parallel_start.elapsed();
380
381 let total_time = total_start.elapsed();
382 let bind_cpu_ms = bind_cpu_time_ns.load(Ordering::Relaxed) as f64 / 1_000_000.0;
383
384 tracing::info!(
385 "binding breakdown: parallel={:.2}ms (bind_cpu={:.2}ms), total={:.2}ms",
386 parallel_time.as_secs_f64() * 1000.0,
387 bind_cpu_ms,
388 total_time.as_secs_f64() * 1000.0,
389 );
390
391 tracing::info!("symbol binding complete");
392}