1use std::{borrow::Cow, fmt::Display};
2
3use dada_ir_ast::{
4 ast::{AstGenericTerm, AstPath, AstPathKind, AstUse, Identifier, SpanVec, SpannedIdentifier},
5 diagnostic::{Diagnostic, Errors, Level, Reported},
6 inputs::Krate,
7 span::{Span, Spanned},
8};
9use dada_util::{FromImpls, boxed_async_fn};
10use salsa::Update;
11use serde::Serialize;
12
13use crate::{
14 check::{CheckTyInEnv, scope_tree::ScopeTreeNode},
15 ir::{
16 binder::BoundTerm,
17 classes::{SymAggregate, SymAggregateStyle, SymClassMember},
18 functions::SymFunction,
19 module::SymModule,
20 primitive::{SymPrimitive, primitives},
21 types::{SymGenericKind, SymGenericTerm},
22 variables::SymVariable,
23 },
24 prelude::Symbol,
25};
26
27use super::Env;
28
29#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct Scope<'scope, 'db> {
32 span: Span<'db>,
33 chain: ScopeChain<'scope, 'db>,
34}
35
36impl<'scope, 'db> Scope<'scope, 'db> {
37 pub(crate) fn new(db: &'db dyn crate::Db, span: Span<'db>) -> Self {
39 let mut this = Scope {
40 span,
41 chain: ScopeChain::primitives(),
42 };
43
44 let root = db.root();
45 let crate_source = root.libdada_crate(db);
46 this = this.with_prelude(db, span, crate_source);
47
48 this
49 }
50
51 pub(crate) fn with_link<'scope1>(
53 self,
54 link: impl Into<ScopeChainKind<'scope1, 'db>>,
55 ) -> Scope<'scope1, 'db>
56 where
57 'scope: 'scope1,
58 {
59 let mut this: Scope<'scope1, 'db> = self;
60 this.push_link(link);
61 this
62 }
63
64 pub fn span(&self) -> Span<'db> {
65 self.span
66 }
67
68 fn with_prelude(self, db: &'db dyn crate::Db, span: Span<'db>, crate_source: Krate) -> Self {
71 let prelude_id = Identifier::prelude(db);
72 match resolve_name_against_crate(
73 db,
74 crate_source,
75 SpannedIdentifier {
76 id: prelude_id,
77 span,
78 },
79 ) {
80 Ok(NameResolutionSym::SymModule(sym)) => self.with_link(ScopeChainKind::SymModule(sym)),
81 Ok(sym) => {
82 let span = sym.span(db).unwrap_or(span);
83 Diagnostic::error(db, span, "prelude is not a module".to_string())
84 .label(
85 db,
86 Level::Error,
87 span,
88 format!(
89 "I expected `prelude` to be a module, but I found {category}",
90 category = sym.categorize(db)
91 ),
92 )
93 .report(db);
94 self
95 }
96 Err(Reported(_)) => self,
97 }
98 }
99
100 pub fn push_link(&mut self, kind: impl Into<ScopeChainKind<'scope, 'db>>) {
102 let chain = ScopeChain {
103 kind: kind.into(),
104 next: None,
105 };
106 let prev_chain = std::mem::replace(&mut self.chain, chain);
107 self.chain.next = Some(Box::new(prev_chain));
108 }
109
110 pub fn aggregate(&self) -> Option<SymAggregate<'db>> {
112 for link in self.chain.iter() {
113 if let ScopeChainKind::SymAggr(aggr) = &link.kind {
114 return Some(*aggr);
115 }
116 }
117 None
118 }
119
120 pub(crate) fn resolve_name(
123 &self,
124 db: &'db dyn crate::Db,
125 id: Identifier<'db>,
126 span: Span<'db>,
127 ) -> Errors<NameResolution<'db>> {
128 if let Some(resolution) = self.chain.iter().find_map(|link| link.resolve_name(db, id)) {
129 return Ok(resolution);
130 }
131
132 Err(
133 Diagnostic::error(db, span, format!("could not find anything named `{id}`",))
134 .label(
135 db,
136 Level::Error,
137 span,
138 "I could not find anything with this name :(",
139 )
140 .report(db),
141 )
142 }
143
144 pub fn generic_sym_in_scope(&self, db: &'db dyn crate::Db, sym: SymVariable<'db>) -> bool {
146 self.chain.iter().any(|link| link.binds_symbol(db, sym))
147 }
148
149 pub fn into_bound_value<B>(self, db: &'db dyn crate::Db, value: B::LeafTerm) -> B
173 where
174 B: BoundTerm<'db>,
175 {
176 let binders = self.all_binders();
178
179 assert!(
181 B::BINDER_LEVELS <= binders.len(),
182 "target type has {} binder levels but the scope only has {}",
183 B::BINDER_LEVELS,
184 binders.len()
185 );
186
187 let extra_binder_levels = binders.len() - B::BINDER_LEVELS;
189 if extra_binder_levels == 0 {
190 return B::bind(db, &mut binders.into_iter(), value);
192 }
193
194 let flattened_binder_levels = extra_binder_levels + 1;
196 let outer_binder = binders
197 .iter()
198 .take(flattened_binder_levels)
199 .flat_map(|v| v.iter().copied())
200 .collect::<Vec<_>>();
201 let remaining_binders = binders.into_iter().skip(flattened_binder_levels);
202 let mut symbols_to_bind = std::iter::once(outer_binder).chain(remaining_binders);
203 B::bind(db, &mut symbols_to_bind, value)
204 }
205
206 pub fn all_binders(&self) -> Vec<Vec<SymVariable<'db>>> {
210 let mut vec = vec![];
211 for link in self.chain.iter() {
212 match &link.kind {
213 ScopeChainKind::Primitives
214 | ScopeChainKind::SymModule(_)
215 | ScopeChainKind::SymAggr(_) => {}
216 ScopeChainKind::ForAll(cow) => {
217 vec.push(cow.iter().copied().collect());
218 }
219 }
220 }
221 vec.reverse();
222 vec
223 }
224}
225
226#[derive(Clone, Debug, PartialEq, Eq)]
230struct ScopeChain<'scope, 'db> {
231 kind: ScopeChainKind<'scope, 'db>,
233
234 next: Option<Box<ScopeChain<'scope, 'db>>>,
236}
237
238#[derive(Clone, Debug, PartialEq, Eq, FromImpls)]
240pub enum ScopeChainKind<'scope, 'db> {
241 #[no_from_impl]
243 Primitives,
244
245 SymModule(SymModule<'db>),
247
248 SymAggr(SymAggregate<'db>),
250
251 ForAll(Cow<'scope, [SymVariable<'db>]>),
253}
254
255impl<'db> From<SymVariable<'db>> for ScopeChainKind<'_, 'db> {
256 fn from(sym: SymVariable<'db>) -> Self {
257 ScopeChainKind::ForAll(Cow::Owned(vec![sym]))
258 }
259}
260
261#[derive(Clone, Debug, PartialEq, Eq, Serialize, Update)]
262pub(crate) struct NameResolution<'db> {
263 pub generics: Vec<SymGenericTerm<'db>>,
264 pub sym: NameResolutionSym<'db>,
265}
266
267impl<'db> NameResolution<'db> {
268 pub fn categorize(&self, db: &'db dyn crate::Db) -> impl Display + 'db {
270 self.sym.categorize(db)
271 }
272
273 #[expect(dead_code)]
275 pub fn describe(&self, db: &'db dyn crate::Db) -> impl Display + 'db {
276 self.sym.describe(db)
277 }
278
279 pub(crate) fn resolve_relative_id(
282 self,
283 db: &'db dyn crate::Db,
284 id: SpannedIdentifier<'db>,
285 ) -> Errors<Result<NameResolution<'db>, NameResolution<'db>>> {
286 match self.sym.resolve_relative_id(db, id) {
287 Ok(Ok(sym)) => Ok(Ok(NameResolution {
288 generics: self.generics,
289 sym,
290 })),
291
292 Ok(Err(s)) => {
293 assert_eq!(self.sym, s);
294 Ok(Err(self))
295 }
296
297 Err(e) => Err(e),
298 }
299 }
300
301 pub(crate) async fn resolve_relative_generic_args(
303 mut self,
304 env: &mut Env<'db>,
305 generics: &SpanVec<'db, AstGenericTerm<'db>>,
306 ) -> Errors<NameResolution<'db>> {
307 let db = env.db();
308
309 let expected_arguments = self.sym.expected_generic_parameters(db);
310 let found_arguments = self.generics.len();
311 assert!(found_arguments <= expected_arguments);
312 let remaining_arguments = expected_arguments - found_arguments;
313
314 if generics.len() > remaining_arguments {
315 let extra_arguments = &generics.values[remaining_arguments..];
316 let extra_span = extra_arguments
317 .first()
318 .unwrap()
319 .span(db)
320 .to(db, extra_arguments.last().unwrap().span(db));
321 return Err(Diagnostic::error(
322 db,
323 extra_span,
324 "extra generic arguments provided".to_string(),
325 )
326 .label(
327 db,
328 Level::Error,
329 extra_span,
330 format!(
331 "I expected to find at most {remaining_arguments} generic arguments, these are extra"
332 ),
333 )
334 .report(db));
335 }
336
337 for v in generics.values.iter() {
338 self.generics.push(v.check_in_env(env).await);
339 }
340
341 Ok(self)
342 }
343
344 pub fn span(&self, db: &'db dyn crate::Db) -> Option<Span<'db>> {
347 self.sym.span(db)
348 }
349}
350
351#[derive(Copy, Clone, Debug, PartialEq, Eq, FromImpls, Serialize, Update)]
353#[allow(clippy::enum_variant_names)]
354pub enum NameResolutionSym<'db> {
355 SymModule(SymModule<'db>),
356 SymAggregate(SymAggregate<'db>),
357 SymFunction(SymFunction<'db>),
358 SymPrimitive(SymPrimitive<'db>),
359 SymVariable(SymVariable<'db>),
360}
361
362impl<'db> NameResolutionSym<'db> {
363 pub fn categorize(self, db: &'db dyn crate::Db) -> impl Display + 'db {
365 match self {
366 NameResolutionSym::SymModule(_) => Box::new("a module") as Box<dyn Display + 'db>,
367 NameResolutionSym::SymAggregate(_) => Box::new("a class"),
368 NameResolutionSym::SymFunction(_) => Box::new("a function"),
369 NameResolutionSym::SymVariable(var) => match var.kind(db) {
370 SymGenericKind::Type => Box::new("a generic type"),
371 SymGenericKind::Perm => Box::new("a generic permission"),
372 SymGenericKind::Place => Box::new("a local variable"),
373 },
374 NameResolutionSym::SymPrimitive(p) => Box::new(format!("`{}`", p.name(db))),
375 }
376 }
377
378 pub(crate) fn resolve_relative_id(
391 self,
392 db: &'db dyn crate::Db,
393 id: SpannedIdentifier<'db>,
394 ) -> Errors<Result<NameResolutionSym<'db>, NameResolutionSym<'db>>> {
395 match self {
396 NameResolutionSym::SymModule(sym_module) => match sym_module
397 .resolve_name_against_definitions(db, id.id)
398 {
399 Some(sym) => Ok(Ok(sym)),
400 None => Err(
401 Diagnostic::error(db, id.span, "nothing named `{}` found in module")
402 .label(
403 db,
404 Level::Error,
405 id.span,
406 format!(
407 "I could not find anything named `{}` in the module `{}`",
408 id.id,
409 sym_module.name(db),
410 ),
411 )
412 .report(db),
413 ),
414 },
415
416 NameResolutionSym::SymAggregate(sym_class) => {
421 match sym_class.inherent_member(db, id.id) {
422 Some(class_member) => match class_member {
423 SymClassMember::SymFunction(sym) => Ok(Ok(sym.into())),
424
425 SymClassMember::SymField(_) => Ok(Err(self)),
427 },
428 None => Ok(Err(self)),
429 }
430 }
431
432 _ => Ok(Err(self)),
433 }
434 }
435
436 pub fn describe(self, db: &'db dyn crate::Db) -> impl Display + 'db {
438 match self {
439 NameResolutionSym::SymModule(sym_module) => {
440 format!("a module named `{}`", sym_module.name(db))
441 }
442 NameResolutionSym::SymAggregate(sym_class) => {
443 format!("a class named `{}`", sym_class.name(db))
444 }
445 NameResolutionSym::SymFunction(sym_function) => {
446 format!("a function named `{}`", sym_function.name(db))
447 }
448 NameResolutionSym::SymVariable(var) => match var.name(db) {
449 Some(n) => format!("{} named `{n}`", self.categorize(db)),
450 None => "an anonymous generic parameter".to_string(),
451 },
452 NameResolutionSym::SymPrimitive(sym_primitive) => {
453 format!("the primitive type `{}`", sym_primitive.name(db))
454 }
455 }
456 }
457
458 fn expected_generic_parameters(&self, db: &'db dyn crate::Db) -> usize {
459 match self {
460 NameResolutionSym::SymModule(sym) => sym.expected_generic_parameters(db),
461 NameResolutionSym::SymAggregate(sym) => sym.expected_generic_parameters(db),
462 NameResolutionSym::SymFunction(sym) => sym.expected_generic_parameters(db),
463 NameResolutionSym::SymPrimitive(_) => 0,
464 NameResolutionSym::SymVariable(_) => 0,
465 }
466 }
467
468 fn span(&self, db: &'db dyn crate::Db) -> Option<Span<'db>> {
469 match self {
470 NameResolutionSym::SymModule(sym) => Some(sym.span(db)),
471 NameResolutionSym::SymAggregate(sym) => Some(sym.span(db)),
472 NameResolutionSym::SymFunction(sym) => Some(sym.span(db)),
473 NameResolutionSym::SymPrimitive(_) => None,
474 NameResolutionSym::SymVariable(sym) => Some(sym.span(db)),
475 }
476 }
477
478 pub fn style(self, db: &'db dyn crate::Db) -> Option<SymAggregateStyle> {
481 match self {
482 NameResolutionSym::SymModule(_) => None,
483 NameResolutionSym::SymAggregate(aggr) => Some(aggr.style(db)),
484 NameResolutionSym::SymFunction(_) => None,
485 NameResolutionSym::SymPrimitive(_) => None,
486 NameResolutionSym::SymVariable(_) => None,
487 }
488 }
489}
490
491pub trait ResolveToSym<'db> {
496 fn resolve_to_sym(
497 self,
498 db: &'db dyn crate::Db,
499 scope: &Scope<'_, 'db>,
500 ) -> Errors<NameResolutionSym<'db>>;
501}
502
503impl<'db> ResolveToSym<'db> for AstPath<'db> {
504 fn resolve_to_sym(
505 self,
506 db: &'db dyn crate::Db,
507 scope: &Scope<'_, 'db>,
508 ) -> Errors<NameResolutionSym<'db>> {
509 match self.kind(db) {
512 AstPathKind::Identifier(first_id) => first_id.resolve_to_sym(db, scope),
513 AstPathKind::GenericArgs { path, args: _ } => path.resolve_to_sym(db, scope),
514 AstPathKind::Member { path, id } => {
515 let base = path.resolve_to_sym(db, scope)?;
516 match base.resolve_relative_id(db, *id)? {
517 Ok(r) => Ok(r),
518 Err(base) => Err(report_path_referencing_field(db, id, base)),
519 }
520 }
521 }
522 }
523}
524
525fn report_path_referencing_field<'db>(
528 db: &'db dyn crate::Db,
529 id: &SpannedIdentifier<'_>,
530 base: NameResolutionSym<'_>,
531) -> Reported {
532 Diagnostic::error(db, id.span, "unexpected `.` in path")
533 .label(
534 db,
535 Level::Error,
536 id.span,
537 format!(
538 "I don't know how to interpret `.` applied to {} here",
539 base.categorize(db),
540 ),
541 )
542 .report(db)
543}
544
545pub trait Resolve<'db> {
549 async fn resolve_in(self, env: &mut Env<'db>) -> Errors<NameResolution<'db>>;
550}
551
552impl<'db> Resolve<'db> for AstPath<'db> {
553 #[boxed_async_fn]
556 async fn resolve_in(self, env: &mut Env<'db>) -> Errors<NameResolution<'db>> {
557 let db = env.db();
560 match self.kind(db) {
561 AstPathKind::Identifier(first_id) => first_id.resolve_in(env).await,
562 AstPathKind::GenericArgs { path, args } => {
563 let base = path.resolve_in(env).await?;
564 base.resolve_relative_generic_args(env, args).await
565 }
566 AstPathKind::Member { path, id } => {
567 let base = path.resolve_in(env).await?;
568 match base.resolve_relative_id(db, *id)? {
569 Ok(r) => Ok(r),
570 Err(base) => Err(report_path_referencing_field(db, id, base.sym)),
571 }
572 }
573 }
574 }
575}
576
577impl<'db> ResolveToSym<'db> for SpannedIdentifier<'db> {
578 fn resolve_to_sym(
579 self,
580 db: &'db dyn crate::Db,
581 scope: &Scope<'_, 'db>,
582 ) -> Errors<NameResolutionSym<'db>> {
583 let NameResolution { sym, generics: _ } = scope.resolve_name(db, self.id, self.span)?;
584 Ok(sym)
585 }
586}
587
588impl<'db> Resolve<'db> for SpannedIdentifier<'db> {
589 async fn resolve_in(self, env: &mut Env<'db>) -> Errors<NameResolution<'db>> {
590 env.scope.resolve_name(env.db(), self.id, self.span)
591 }
592}
593
594impl<'scope, 'db> ScopeChain<'scope, 'db> {
595 fn primitives() -> Self {
597 ScopeChain {
598 kind: ScopeChainKind::Primitives,
599 next: None,
600 }
601 }
602
603 pub fn iter(&self) -> impl Iterator<Item = &ScopeChain<'scope, 'db>> {
605 let mut p = Some(self);
606
607 std::iter::from_fn(move || match p.take() {
608 Some(q) => {
609 if let Some(n) = &q.next {
610 p = Some(n);
611 } else {
612 p = None;
613 }
614
615 Some(q)
616 }
617 None => None,
618 })
619 }
620
621 fn resolve_name(
622 &self,
623 db: &'db dyn crate::Db,
624 id: Identifier<'db>,
625 ) -> Option<NameResolution<'db>> {
626 match &self.kind {
627 ScopeChainKind::Primitives => primitives(db)
628 .iter()
629 .copied()
630 .filter(|p| p.name(db) == id)
631 .map(|p| NameResolution {
632 generics: vec![],
633 sym: p.into(),
634 })
635 .next(),
636
637 ScopeChainKind::SymAggr(_) => None,
638
639 ScopeChainKind::SymModule(sym) => {
640 if let Some(sym) = sym.resolve_name_against_definitions(db, id) {
644 match sym {
645 NameResolutionSym::SymModule(sym) => {
646 Some(self.internal_module_item(db, sym))
647 }
648 NameResolutionSym::SymAggregate(sym) => {
649 Some(self.internal_module_item(db, sym))
650 }
651 NameResolutionSym::SymFunction(sym) => {
652 Some(self.internal_module_item(db, sym))
653 }
654 NameResolutionSym::SymPrimitive(_) | NameResolutionSym::SymVariable(_) => {
655 unreachable!()
657 }
658 }
659 } else {
660 sym.resolve_name_against_uses(db, id)
661 }
662 }
663
664 ScopeChainKind::ForAll(symbols) => {
665 if let Some(index) = symbols.iter().position(|&s| s.name(db) == Some(id)) {
666 let sym = symbols[index];
667 Some(NameResolution {
668 generics: vec![],
669 sym: NameResolutionSym::SymVariable(sym),
670 })
671 } else {
672 None
673 }
674 }
675 }
676 }
677
678 fn internal_module_item(
680 &self,
681 _db: &'db dyn crate::Db,
682 sym: impl ScopeTreeNode<'db> + Into<NameResolutionSym<'db>> + Copy,
683 ) -> NameResolution<'db> {
684 NameResolution {
685 generics: vec![],
687 sym: sym.into(),
688 }
689 }
690
691 fn binds_symbol(&self, _db: &'db dyn crate::Db, sym: SymVariable<'db>) -> bool {
692 match &self.kind {
693 ScopeChainKind::SymAggr(_)
694 | ScopeChainKind::Primitives
695 | ScopeChainKind::SymModule(_) => false,
696
697 ScopeChainKind::ForAll(symbols) => symbols.contains(&sym),
698 }
699 }
700}
701
702impl<'db> SymModule<'db> {
703 fn resolve_name_against_definitions(
704 self,
705 db: &'db dyn crate::Db,
706 id: Identifier<'db>,
707 ) -> Option<NameResolutionSym<'db>> {
708 if let Some(&v) = self.class_map(db).get(&id) {
709 return Some(v.into());
710 }
711
712 if let Some(&v) = self.function_map(db).get(&id) {
713 return Some(v.into());
714 }
715
716 None
717 }
718
719 fn resolve_name_against_uses(
720 self,
721 db: &'db dyn crate::Db,
722 id: Identifier<'db>,
723 ) -> Option<NameResolution<'db>> {
724 let ast_use = self.ast_use_map(db).get(&id)?;
725 resolve_ast_use(db, *ast_use)
726 }
727}
728
729#[salsa::tracked]
730fn resolve_ast_use<'db>(
731 db: &'db dyn crate::Db,
732 ast_use: AstUse<'db>,
733) -> Option<NameResolution<'db>> {
734 let crate_name = ast_use.crate_name(db);
735 let Some(crate_source) = db.root().crate_source(db, crate_name.id) else {
736 Diagnostic::error(
737 db,
738 crate_name.span,
739 format!(
740 "could not find a crate named `{}`",
741 ast_use.crate_name(db).id
742 ),
743 )
744 .label(
745 db,
746 Level::Error,
747 crate_name.span,
748 "could not find this crate",
749 )
750 .report(db);
751 return None;
752 };
753
754 ast_use
755 .path(db)
756 .resolve_against(db, |id| resolve_name_against_crate(db, crate_source, id))
757}
758
759fn resolve_name_against_crate<'db>(
760 db: &'db dyn crate::Db,
761 krate: Krate,
762 id: SpannedIdentifier<'db>,
763) -> Errors<NameResolutionSym<'db>> {
764 let source_file = db.source_file(krate, &[id.id]);
765 match source_file.contents(db) {
766 Ok(_) => {
767 let sym_module = source_file.symbol(db);
768 Ok(sym_module.into())
769 }
770
771 Err(message) => Err(Diagnostic::new(db, Level::Error, id.span(db), message).report(db)),
772 }
773}
774
775trait ResolveAgainst<'db> {
776 fn resolve_against(
777 self,
778 db: &'db dyn crate::Db,
779 op: impl FnOnce(SpannedIdentifier<'db>) -> Errors<NameResolutionSym<'db>>,
780 ) -> Option<NameResolution<'db>>;
781}
782
783impl<'db> ResolveAgainst<'db> for AstPath<'db> {
784 fn resolve_against(
785 self,
786 _db: &'db dyn crate::Db,
787 _op: impl FnOnce(SpannedIdentifier<'db>) -> Errors<NameResolutionSym<'db>>,
788 ) -> Option<NameResolution<'db>> {
789 todo!()
790 }
791}