Skip to main content

flux_desugar/
resolver.rs

1pub(crate) mod refinement_resolver;
2
3use std::collections::hash_map;
4
5use flux_common::{
6    bug,
7    result::{ErrorCollector, ResultExt},
8};
9use flux_errors::Errors;
10use flux_middle::{
11    ResolverOutput, Specs,
12    def_id::{FluxDefId, FluxLocalDefId, MaybeExternId},
13    fhir,
14    fhir::{
15        Namespace::{self, *},
16        PerNS,
17    },
18    global_env::GlobalEnv,
19};
20use flux_syntax::{
21    surface::{self, Ident, visit::Visitor as _},
22    symbols::sym,
23};
24use hir::{ItemId, ItemKind, OwnerId, def::DefKind};
25use itertools::Itertools;
26use rustc_data_structures::unord::{ExtendUnord, UnordMap};
27use rustc_errors::ErrorGuaranteed;
28use rustc_hir::{
29    self as hir, CRATE_HIR_ID, CRATE_OWNER_ID, ParamName, PrimTy, def::CtorOf, def_id::CRATE_DEF_ID,
30};
31use rustc_middle::{metadata::ModChild, ty::TyCtxt};
32use rustc_span::{Span, Symbol, def_id::DefId, symbol::kw};
33
34use self::refinement_resolver::RefinementResolver;
35
36type Result<T = ()> = std::result::Result<T, ErrorGuaranteed>;
37
38/// The reason a name lookup (`resolve_ident_with_ribs`, `resolve_ident_in_module`,
39/// `resolve_path_with_ribs`) failed.
40enum ResolveError {
41    NotFound,
42    /// The name resolved to two or more distinct, competing bindings (e.g. two different glob
43    /// imports bringing in different items under the same name) with nothing more specific to
44    /// disambiguate. Carries everything needed to emit [`errors::AmbiguousName`], so that callers
45    /// deep in the path walk don't have to reconstruct which segment went wrong.
46    Ambiguous(Ambiguity),
47}
48
49/// A name that two competing glob imports bind to different items:
50///
51/// ```ignore
52/// mod a { pub struct S; }
53/// mod b { pub struct S; }
54///
55/// use a::*;
56/// use b::*;
57///
58/// #[flux::spec(fn(x: S))]
59/// fn f(x: a::S) {}
60/// ```
61#[derive(Clone, Copy, Debug)]
62struct Ambiguity {
63    /// The use site that hit the ambiguity: the `S` inside the signature.
64    span: Span,
65    /// The contested name: `S`.
66    name: Symbol,
67    /// Where the first candidate was brought into scope: the `use a::*;` statement.
68    first: Span,
69    /// Same for the second candidate: `use b::*;`. It can coincide with `first` when a single
70    /// glob imports a module that is itself ambiguously re-exporting the name.
71    second: Span,
72}
73
74pub(crate) fn resolve_crate(genv: GlobalEnv) -> ResolverOutput {
75    match try_resolve_crate(genv) {
76        Ok(output) => output,
77        Err(err) => genv.sess().abort(err),
78    }
79}
80
81fn try_resolve_crate(genv: GlobalEnv) -> Result<ResolverOutput> {
82    let specs = genv.collect_specs();
83    let mut resolver = CrateResolver::new(genv, specs);
84
85    genv.tcx().hir_walk_toplevel_module(&mut resolver);
86
87    resolver.into_output()
88}
89
90pub(crate) struct CrateResolver<'genv, 'tcx> {
91    genv: GlobalEnv<'genv, 'tcx>,
92    specs: &'genv Specs,
93    output: ResolverOutput,
94    ribs: PerNS<Vec<Rib>>,
95    /// A mapping from the names of all imported crates to their [`DefId`]
96    crates: UnordMap<Symbol, DefId>,
97    /// Names available everywhere: Rust builtin types plus flux's global funcs (theory funcs, `cast`)
98    /// and primitive sorts.
99    prelude: PerNS<Rib>,
100    qualifiers: UnordMap<Symbol, FluxLocalDefId>,
101    primop_props: UnordMap<Symbol, FluxDefId>,
102    err: Option<ErrorGuaranteed>,
103    /// The most recent module we have visited. Used to check for visibility of other items from
104    /// this module.
105    current_module: OwnerId,
106}
107
108/// Map to keep track of names defined in a scope
109#[derive(Default)]
110struct DefinitionMap {
111    defined: UnordMap<Ident, ()>,
112}
113
114impl DefinitionMap {
115    fn define(&mut self, name: Ident) -> std::result::Result<(), errors::DuplicateDefinition> {
116        match self.defined.entry(name) {
117            hash_map::Entry::Occupied(entry) => {
118                Err(errors::DuplicateDefinition {
119                    span: name.span,
120                    previous_definition: entry.key().span,
121                    name: name.name,
122                })
123            }
124            hash_map::Entry::Vacant(entry) => {
125                entry.insert(());
126                Ok(())
127            }
128        }
129    }
130}
131
132impl<'genv, 'tcx> CrateResolver<'genv, 'tcx> {
133    pub fn new(genv: GlobalEnv<'genv, 'tcx>, specs: &'genv Specs) -> Self {
134        Self {
135            genv,
136            output: ResolverOutput::default(),
137            specs,
138            ribs: PerNS { type_ns: vec![], value_ns: vec![], macro_ns: vec![], flux_fn_ns: vec![] },
139            crates: mk_crate_mapping(genv.tcx()),
140            prelude: PerNS {
141                type_ns: builtin_types_rib(),
142                value_ns: Rib::new(RibKind::Misc),
143                macro_ns: Rib::new(RibKind::Misc),
144                flux_fn_ns: theory_funcs_rib(),
145            },
146            err: None,
147            qualifiers: Default::default(),
148            primop_props: Default::default(),
149            current_module: CRATE_OWNER_ID,
150        }
151    }
152
153    /// Qualifiers and primop-props are global, so their names are checked for duplicates across
154    /// the whole crate (unlike other flux items, which are scoped per-module via [`Self::define_res_in`]).
155    #[allow(
156        clippy::disallowed_methods,
157        reason = "`flux_items_by_parent` is the source of truth for `FluxDefId`"
158    )]
159    fn define_flux_global_items(&mut self) {
160        let mut definitions = DefinitionMap::default();
161        for (parent, items) in &self.specs.flux_items_by_parent {
162            for item in items {
163                // We are putting qualifiers and primpops in the same namespace.
164                match item {
165                    surface::FluxItem::Qualifier(qual) => {
166                        let ident = qual.name;
167                        if definitions
168                            .define(ident)
169                            .emit(&self.genv)
170                            .collect_err(&mut self.err)
171                            .is_some()
172                        {
173                            let def_id = FluxLocalDefId::new(parent.def_id, ident.name);
174                            self.qualifiers.insert(ident.name, def_id);
175                        }
176                    }
177                    surface::FluxItem::PrimOpProp(primop) => {
178                        let ident = primop.name;
179                        if definitions
180                            .define(ident)
181                            .emit(&self.genv)
182                            .collect_err(&mut self.err)
183                            .is_some()
184                        {
185                            let def_id = FluxDefId::new(parent.def_id.to_def_id(), ident.name);
186                            self.primop_props.insert(ident.name, def_id);
187                        }
188                    }
189                    surface::FluxItem::Use(_)
190                    | surface::FluxItem::FuncDef(_)
191                    | surface::FluxItem::SortDecl(_) => {}
192                };
193            }
194        }
195    }
196
197    #[allow(
198        clippy::disallowed_methods,
199        reason = "`flux_items_by_parent` is the source of truth for `FluxDefId`"
200    )]
201    fn define_module_flux_items(&mut self, parent: OwnerId) {
202        let Some(items) = self.specs.flux_items_by_parent.get(&parent) else { return };
203        for item in items {
204            match item {
205                surface::FluxItem::Qualifier(_) | surface::FluxItem::PrimOpProp(_) => {
206                    // Already registered in `define_global_qualifiers_and_primop_props`.
207                }
208                surface::FluxItem::FuncDef(defn) => {
209                    let def_id = FluxDefId::new(parent.def_id.to_def_id(), defn.name.name);
210                    let kind = fhir::SpecFuncKind::Def(def_id);
211                    self.define_res_in(
212                        fhir::Res::GlobalFunc(kind),
213                        ReftNS,
214                        BindingSource::Explicit(defn.name),
215                    );
216                }
217                surface::FluxItem::SortDecl(sort_decl) => {
218                    let def_id = FluxDefId::new(parent.def_id.to_def_id(), sort_decl.name.name);
219                    self.define_res_in(
220                        fhir::Res::UserSort(def_id),
221                        TypeNS,
222                        BindingSource::Explicit(sort_decl.name),
223                    );
224                }
225                surface::FluxItem::Use(use_tree) => {
226                    // Flux's `use` has no glob form, so every name it brings in is explicit.
227                    for (ident, res, ns) in self.resolve_flux_use_tree(use_tree) {
228                        self.define_res_in(res, ns, BindingSource::Explicit(ident));
229                    }
230                }
231            }
232        }
233    }
234
235    fn define_items(&mut self, item_ids: impl IntoIterator<Item = &'tcx ItemId>) {
236        for item_id in item_ids {
237            let item = self.genv.tcx().hir_item(*item_id);
238            let def_kind = match item.kind {
239                ItemKind::Use(path, kind) => {
240                    match kind {
241                        hir::UseKind::Single(ident) => {
242                            if let Some(res) = path.res.value_ns
243                                && let Ok(res) = fhir::Res::try_from(res)
244                            {
245                                self.define_res_in(res, ValueNS, BindingSource::Explicit(ident));
246                            }
247                            if let Some(res) = path.res.type_ns
248                                && let Ok(res) = fhir::Res::try_from(res)
249                            {
250                                self.define_res_in(res, TypeNS, BindingSource::Explicit(ident));
251                            }
252                        }
253                        hir::UseKind::Glob => {
254                            let is_prelude = is_prelude_import(self.genv.tcx(), item);
255                            let glob_span = item.span;
256                            for mod_child in self.glob_imports(path) {
257                                if let Ok(res) = fhir::Res::try_from(mod_child.res)
258                                    && let Some(ns @ (TypeNS | ValueNS)) = res.ns()
259                                {
260                                    if is_prelude {
261                                        self.define_in_prelude(mod_child.ident, res, ns);
262                                    } else {
263                                        let source = BindingSource::Glob {
264                                            ident: mod_child.ident,
265                                            glob_span,
266                                        };
267                                        self.define_res_in(res, ns, source);
268                                    }
269                                }
270                            }
271                        }
272                        hir::UseKind::ListStem => {}
273                    }
274                    continue;
275                }
276                ItemKind::TyAlias(..) => DefKind::TyAlias,
277                ItemKind::Enum(..) => DefKind::Enum,
278                ItemKind::Struct(..) => DefKind::Struct,
279                ItemKind::Union(..) => DefKind::Union,
280                ItemKind::Trait(..) => DefKind::Trait,
281                ItemKind::Mod(..) => DefKind::Mod,
282                ItemKind::Const(..) => DefKind::Const,
283                ItemKind::ForeignMod { items, .. } => {
284                    self.define_foreign_items(items);
285                    continue;
286                }
287                _ => continue,
288            };
289            if let Some(ns) = def_kind.ns().map(Namespace::from)
290                && let Some(ident) = item.kind.ident()
291            {
292                self.define_res_in(
293                    fhir::Res::Def(def_kind, item.owner_id.to_def_id()),
294                    ns,
295                    BindingSource::Explicit(ident),
296                );
297            }
298        }
299    }
300
301    fn define_foreign_items(&mut self, items: &[rustc_hir::ForeignItemId]) {
302        for item_id in items {
303            let item = self.genv.tcx().hir_foreign_item(*item_id);
304            match item.kind {
305                rustc_hir::ForeignItemKind::Type => {
306                    self.define_res_in(
307                        fhir::Res::Def(DefKind::ForeignTy, item.owner_id.to_def_id()),
308                        TypeNS,
309                        BindingSource::Explicit(item.ident),
310                    );
311                }
312                rustc_hir::ForeignItemKind::Fn(..) | rustc_hir::ForeignItemKind::Static(..) => {}
313            }
314        }
315    }
316
317    /// Define the name in `source` in the innermost rib of `ns`.
318    fn define_res_in(
319        &mut self,
320        res: fhir::Res<surface::NodeId>,
321        ns: Namespace,
322        source: BindingSource,
323    ) {
324        let ident = source.ident();
325        if ident.name == kw::Underscore {
326            return;
327        }
328        let entry = self.ribs[ns]
329            .last_mut()
330            .unwrap()
331            .bindings
332            .entry(ident.name)
333            .or_default();
334
335        if let Some(prev) = entry.define(source, res) {
336            if let fhir::Res::Param(..) = prev.res {
337                self.emit(errors::DuplicateParam {
338                    span: ident.span,
339                    name: ident.name,
340                    first_use: prev.span,
341                });
342            } else {
343                self.emit(errors::DuplicateDefinition {
344                    span: ident.span,
345                    previous_definition: prev.span,
346                    name: ident.name,
347                });
348            }
349        }
350    }
351
352    /// Define `ident` in the prelude of `ns`. The prelude is only ever populated from the single
353    /// `#[prelude_import]` glob, so unlike [`Self::define_res_in`] it keeps plain last-one-wins
354    /// semantics: there is no second glob to be ambiguous with.
355    fn define_in_prelude(&mut self, ident: Ident, res: fhir::Res<surface::NodeId>, ns: Namespace) {
356        self.prelude[ns]
357            .bindings
358            .insert(ident.name, NameResolution::from_explicit(Binding { span: ident.span, res }));
359    }
360
361    fn push_rib(&mut self, ns: Namespace, kind: RibKind) {
362        self.ribs[ns].push(Rib::new(kind));
363    }
364
365    fn pop_rib(&mut self, ns: Namespace) {
366        self.ribs[ns].pop();
367    }
368
369    fn define_generics(&mut self, def_id: MaybeExternId<OwnerId>) {
370        let generics = self
371            .genv
372            .tcx()
373            .hir_get_generics(def_id.local_id().def_id)
374            .unwrap();
375        for param in generics.params {
376            let def_kind = self.genv.tcx().def_kind(param.def_id);
377            if let ParamName::Plain(name) = param.name
378                && let Some(ns) = def_kind.ns().map(Namespace::from)
379            {
380                debug_assert!(matches!(def_kind, DefKind::TyParam | DefKind::ConstParam));
381                let param_id = self.genv.maybe_extern_id(param.def_id).resolved_id();
382                self.define_res_in(
383                    fhir::Res::Def(def_kind, param_id),
384                    ns,
385                    BindingSource::Explicit(name),
386                );
387            }
388        }
389    }
390
391    fn resolve_flux_items(&mut self, parent: OwnerId) {
392        let Some(items) = self.specs.flux_items_by_parent.get(&parent) else { return };
393        for item in items {
394            RefinementResolver::resolve_flux_item(self, item).collect_err(&mut self.err);
395        }
396    }
397
398    fn resolve_item(&mut self, item: &surface::Item, item_id: MaybeExternId<OwnerId>) -> Result {
399        ItemResolver::run(self, item_id, |item_resolver| item_resolver.visit_item(item))?;
400        RefinementResolver::resolve_item(self, item)
401    }
402
403    fn resolve_trait_item(
404        &mut self,
405        item: &surface::TraitItemFn,
406        item_id: MaybeExternId<OwnerId>,
407    ) -> Result {
408        ItemResolver::run(self, item_id, |item_resolver| item_resolver.visit_trait_item(item))?;
409        RefinementResolver::resolve_trait_item(self, item)
410    }
411
412    fn resolve_impl_item(
413        &mut self,
414        item: &surface::ImplItemFn,
415        item_id: MaybeExternId<OwnerId>,
416    ) -> Result {
417        ItemResolver::run(self, item_id, |item_resolver| item_resolver.visit_impl_item(item))?;
418        RefinementResolver::resolve_impl_item(self, item)
419    }
420
421    fn resolve_path_with_ribs<S: Segment>(
422        &mut self,
423        segments: &[S],
424        ns: Namespace,
425    ) -> std::result::Result<fhir::PartialRes<surface::NodeId>, ResolveError> {
426        let mut module: Option<Module> = None;
427        for (segment_idx, segment) in segments.iter().enumerate() {
428            let is_last = segment_idx + 1 == segments.len();
429            let ns = if is_last { ns } else { TypeNS };
430
431            let base_res = if let Some(module) = module {
432                self.resolve_ident_in_module(module, segment.ident(), ns)?
433            } else {
434                self.resolve_ident_with_ribs(segment.ident(), ns)?
435            };
436
437            S::record_segment_res(self, segment, base_res);
438
439            if is_last {
440                return Ok(fhir::PartialRes::new(base_res));
441            }
442
443            match base_res {
444                fhir::Res::Def(DefKind::Mod, module_id) => {
445                    module = Some(Module::new(ModuleKind::Mod, module_id));
446                }
447                fhir::Res::Def(DefKind::Trait, module_id) => {
448                    module = Some(Module::new(ModuleKind::Trait, module_id));
449                }
450                fhir::Res::Def(DefKind::Enum, module_id) => {
451                    module = Some(Module::new(ModuleKind::Enum, module_id));
452                }
453                _ => {
454                    return Ok(fhir::PartialRes::with_unresolved_segments(
455                        base_res,
456                        segments.len() - segment_idx - 1,
457                    ));
458                }
459            }
460        }
461        Err(ResolveError::NotFound)
462    }
463
464    fn resolve_flux_use_tree(
465        &mut self,
466        use_tree: &surface::UseTree,
467    ) -> Vec<(Ident, fhir::Res<surface::NodeId>, Namespace)> {
468        self.resolve_flux_use_tree_rec(use_tree, None, &mut vec![])
469    }
470
471    fn resolve_flux_use_tree_rec(
472        &mut self,
473        use_tree: &surface::UseTree,
474        mut resolved_module_id: Option<DefId>,
475        resolved_prefix: &mut Vec<Ident>,
476    ) -> Vec<(Ident, fhir::Res<surface::NodeId>, Namespace)> {
477        let Some((last, all_but_last)) = use_tree.prefix.segments.split_last() else {
478            bug!("path must have at least one segment")
479        };
480        let module_segments = match &use_tree.kind {
481            surface::UseTreeKind::Simple => all_but_last,
482            surface::UseTreeKind::Nested(_) => &use_tree.prefix.segments[..],
483        };
484
485        for segment in module_segments {
486            let ident = segment.ident();
487            let res = if let Some(module_id) = resolved_module_id {
488                let module = Module::new(ModuleKind::Mod, module_id);
489                self.resolve_ident_in_module(module, ident, TypeNS)
490            } else {
491                self.resolve_ident_with_ribs(ident, TypeNS)
492            };
493            let res = match res {
494                Ok(res) => res,
495                Err(ResolveError::NotFound) => {
496                    self.emit_unresolved_import(ident, resolved_module_id, resolved_prefix);
497                    return vec![];
498                }
499                Err(ResolveError::Ambiguous(ambiguity)) => {
500                    self.emit_ambiguity_err(ambiguity);
501                    return vec![];
502                }
503            };
504            if let fhir::Res::Def(DefKind::Mod, def_id) = res {
505                resolved_module_id = Some(def_id);
506            } else {
507                self.emit_not_a_module(ident, resolved_prefix);
508                return vec![];
509            }
510            resolved_prefix.push(ident);
511        }
512
513        match &use_tree.kind {
514            surface::UseTreeKind::Simple => {
515                let mut resolutions = vec![];
516                // An import names an item in every namespace at once, so an ambiguity in any one
517                // of them is an error even if the others resolve. Keeping only the first means one
518                // import is one error.
519                let mut ambiguity = None;
520                for ns in [TypeNS, ValueNS, ReftNS] {
521                    let res = if let Some(module_id) = resolved_module_id {
522                        let module = Module::new(ModuleKind::Mod, module_id);
523                        self.resolve_ident_in_module(module, last.ident(), ns)
524                    } else {
525                        self.resolve_ident_with_ribs(last.ident(), ns)
526                    };
527                    match res {
528                        Ok(res) => resolutions.push((last.ident(), res, ns)),
529                        Err(ResolveError::Ambiguous(amb)) => ambiguity = ambiguity.or(Some(amb)),
530                        Err(ResolveError::NotFound) => {}
531                    }
532                }
533                if let Some(ambiguity) = ambiguity {
534                    self.emit_ambiguity_err(ambiguity);
535                } else if resolutions.is_empty() {
536                    self.emit_unresolved_import(last.ident, resolved_module_id, resolved_prefix);
537                }
538                resolutions
539            }
540            surface::UseTreeKind::Nested(items) => {
541                let mut resolutions = vec![];
542                for item in items {
543                    let len = resolved_prefix.len();
544                    resolutions.extend(self.resolve_flux_use_tree_rec(
545                        item,
546                        resolved_module_id,
547                        resolved_prefix,
548                    ));
549                    resolved_prefix.truncate(len);
550                }
551                resolutions
552            }
553        }
554    }
555
556    fn resolve_ident_with_ribs(
557        &self,
558        ident: Ident,
559        ns: Namespace,
560    ) -> std::result::Result<fhir::Res<surface::NodeId>, ResolveError> {
561        let mut ribs = self.ribs[ns].iter().rev();
562        while let Some(rib) = ribs.next() {
563            // An ambiguous name is still a name: it stops the climb here rather than falling
564            // through to outer scopes or the prelude.
565            if let Some(name_res) = rib.bindings.get(&ident.name)
566                && let Some(res) = name_res.resolve(ident)
567            {
568                return res.map_err(ResolveError::Ambiguous);
569            }
570            match rib.kind {
571                // A module boundary stops item resolution.
572                RibKind::Module => break,
573                // A variant is a barrier that hides refinement params bound in the `refined_by` *enclosing*
574                // scope, but not other refinement bindings in other scopes (e.g. flux funcs in the parent module).
575                // FIXME: this is skipping potential names defined inside a `const _ = { ... }` or other similar
576                // "transparent modules".
577                RibKind::Variant => {
578                    ribs.take_while_ref(|rib| !matches!(rib.kind, RibKind::Module))
579                        .for_each(drop);
580                }
581                _ => {}
582            }
583        }
584        if ns == TypeNS {
585            if let Some(crate_id) = self.crates.get(&ident.name) {
586                return Ok(fhir::Res::Def(DefKind::Mod, *crate_id));
587            }
588            // FIXME: `crate` and `super` should only be allowed as the first segment
589            if ident.name == kw::Crate {
590                return Ok(fhir::Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()));
591            }
592            if ident.name == kw::Super
593                && let Some(parent) = self.genv.tcx().opt_local_parent(self.current_module.def_id)
594            {
595                return Ok(fhir::Res::Def(DefKind::Mod, parent.to_def_id()));
596            }
597        }
598
599        if let Some(name_res) = self.prelude[ns].bindings.get(&ident.name)
600            && let Some(res) = name_res.resolve(ident)
601        {
602            return res.map_err(ResolveError::Ambiguous);
603        }
604        Err(ResolveError::NotFound)
605    }
606
607    fn glob_imports(
608        &mut self,
609        path: &hir::UsePath,
610    ) -> impl Iterator<Item = &'tcx ModChild> + use<'tcx> {
611        // The path for the prelude import is not resolved anymore after <https://github.com/rust-lang/rust/pull/145322>,
612        // so we resolve all paths here. If this ever causes problems, we could use the resolution in the `UsePath` for
613        // non-prelude glob imports.
614        let tcx = self.genv.tcx();
615        let curr_mod = self.current_module.to_def_id();
616        self.resolve_path_with_ribs(path.segments, TypeNS)
617            .ok()
618            .and_then(|partial_res| partial_res.full_res())
619            .and_then(|res| {
620                if let fhir::Res::Def(DefKind::Mod, module_id) = res {
621                    Some(module_id)
622                } else {
623                    None
624                }
625            })
626            .into_iter()
627            .flat_map(move |module_id| visible_module_children(tcx, module_id, curr_mod))
628    }
629
630    fn resolve_ident_in_module(
631        &self,
632        module: Module,
633        ident: Ident,
634        ns: Namespace,
635    ) -> std::result::Result<fhir::Res<surface::NodeId>, ResolveError> {
636        let tcx = self.genv.tcx();
637        let res = match module.kind {
638            ModuleKind::Mod => {
639                let module_id = module.def_id;
640                let current_mod = self.current_module.to_def_id();
641
642                // Three sources, tried in order, first one with the name wins: the module's Rust
643                // children, then the ambiguous ones rustc leaves out of `module_children`, then
644                // its flux items. Only the first two have a Rust namespace to look in.
645                //
646                // Keeping them apart matters because the folds below read every candidate as a
647                // glob, so one pool would make `mod m { struct Bag; defs! { opaque sort Bag; } }`
648                // ambiguous at each `m::Bag`. It isn't: two explicit definitions clash as a
649                // duplicate definition, already reported when `m`'s rib was built.
650                let mut resolution: NameResolution = ns
651                    .to_rustc()
652                    .into_iter()
653                    .flat_map(|rustc_ns| {
654                        visible_module_children(tcx, module_id, current_mod).filter(move |child| {
655                            child.res.matches_ns(rustc_ns)
656                                && tcx.hygienic_eq(ident, child.ident, current_mod)
657                        })
658                    })
659                    .filter_map(|child| {
660                        Some(Binding {
661                            span: child.ident.span,
662                            res: fhir::Res::try_from(child.res).ok()?,
663                        })
664                    })
665                    .collect();
666                if resolution.is_empty() {
667                    // Still a Rust name, so it comes before any flux item.
668                    if let Some(ambiguity) = self.ambiguous_module_child(module_id, ident, ns) {
669                        return Err(ResolveError::Ambiguous(ambiguity));
670                    }
671                    resolution = self
672                        .genv
673                        .flux_module_children(module_id)
674                        .iter()
675                        .filter(|child| {
676                            child.res.ns() == Some(ns) && child.ident.name == ident.name
677                        })
678                        .map(|child| {
679                            Binding {
680                                span: child.ident.span,
681                                res: child.res.map_param_id(|id| match id {}),
682                            }
683                        })
684                        .collect();
685                }
686                resolution.resolve(ident)
687            }
688            ModuleKind::Trait => {
689                // Associated items are Rust items, so we only ever resolve them in a Rust namespace.
690                ns.to_rustc()
691                    .and_then(|rustc_ns| {
692                        let trait_id = module.def_id;
693                        tcx.associated_items(trait_id)
694                            .find_by_ident_and_namespace(tcx, ident, rustc_ns, trait_id)
695                            .map(|assoc| fhir::Res::Def(assoc.kind.as_def_kind(), assoc.def_id))
696                    })
697                    .map(Ok)
698            }
699            ModuleKind::Enum => {
700                tcx.adt_def(module.def_id)
701                    .variants()
702                    .iter()
703                    .find(|data| data.name == ident.name)
704                    .and_then(|data| {
705                        let (kind, def_id) = match (ns, data.ctor) {
706                            (TypeNS, _) => (DefKind::Variant, data.def_id),
707                            (ValueNS, Some((ctor_kind, ctor_id))) => {
708                                (DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_id)
709                            }
710                            _ => return None,
711                        };
712                        Some(fhir::Res::Def(kind, def_id))
713                    })
714                    .map(Ok)
715            }
716        };
717        match res {
718            Some(res) => res.map_err(ResolveError::Ambiguous),
719            None => Err(ResolveError::NotFound),
720        }
721    }
722
723    /// The ambiguity for `ident` in `module_id`, if the module's *own* glob re-exports bind the
724    /// name to two different items (`mod m { pub use a::*; pub use b::*; }`). rustc keeps such
725    /// bindings out of `module_children` — they go into a separate `ambig_module_children` map —
726    /// so they need their own lookup.
727    ///
728    /// Only *local* modules are covered. The data for a foreign module is reachable only through an
729    /// untracked `CStore` accessor, and rustc doesn't report `E0659` across a crate boundary
730    /// anyway: it resolves to the first candidate and fires the `ambiguous_glob_imports`
731    /// future-incompatibility lint instead (see rust-lang/rust#114095). We report those as
732    /// unresolved.
733    #[expect(clippy::disallowed_methods, reason = "modules cannot have extern specs")]
734    fn ambiguous_module_child(
735        &self,
736        module_id: DefId,
737        ident: Ident,
738        ns: Namespace,
739    ) -> Option<Ambiguity> {
740        let tcx = self.genv.tcx();
741        let module_id = module_id.as_local()?;
742        let rustc_ns = ns.to_rustc()?;
743        let current_mod = self.current_module.to_def_id();
744        let child = tcx
745            .resolutions(())
746            .ambig_module_children
747            .get(&module_id)?
748            .iter()
749            .find(|child| {
750                child.main.res.matches_ns(rustc_ns)
751                    && tcx.hygienic_eq(ident, child.main.ident, current_mod)
752                    && child.main.vis.is_accessible_from(current_mod, tcx)
753            })?;
754        Some(Ambiguity {
755            span: ident.span,
756            name: ident.name,
757            first: self.glob_span(&child.main),
758            second: self.glob_span(&child.second),
759        })
760    }
761
762    /// Where a module child was brought into its module: the `use ...::*;` statement it was
763    /// re-exported by, or the item's own definition. Mirrors rustc's `child_span`.
764    fn glob_span(&self, child: &ModChild) -> Span {
765        let def_id = child
766            .reexport_chain
767            .first()
768            .and_then(|reexport| reexport.id())
769            .unwrap_or_else(|| child.res.def_id());
770        self.genv.tcx().def_span(def_id)
771    }
772
773    pub fn into_output(self) -> Result<ResolverOutput> {
774        self.err.into_result()?;
775        Ok(self.output)
776    }
777
778    #[track_caller]
779    fn emit_unresolved_import(
780        &mut self,
781        ident: Ident,
782        module_id: Option<DefId>,
783        resolved_prefix: &[Ident],
784    ) {
785        let reason = match module_id {
786            None => "not found in this scope".to_string(),
787            Some(_) => format!("no `{ident}` in `{}`", Segment::format_iter(resolved_prefix)),
788        };
789        let name = Segment::format_iter(resolved_prefix.iter().chain(&[ident]));
790        self.emit(errors::UnresolvedImport { span: ident.span, name, reason });
791    }
792
793    #[track_caller]
794    fn emit_ambiguity_err(&mut self, ambiguity: Ambiguity) {
795        self.emit(errors::AmbiguousName::new(ambiguity));
796    }
797
798    fn emit_not_a_module(&mut self, ident: Ident, resolved_prefix: &[Ident]) {
799        let name = Segment::format_iter(resolved_prefix.iter().chain(&[ident]));
800        self.emit(errors::UnresolvedImport {
801            span: ident.span,
802            name,
803            reason: format!("`{ident}` is not a module"),
804        });
805    }
806
807    #[track_caller]
808    fn emit(&mut self, err: impl rustc_errors::Diagnostic<'genv>) {
809        self.err.collect(self.genv.sess().emit_err(err));
810    }
811}
812
813impl<'tcx> hir::intravisit::Visitor<'tcx> for CrateResolver<'_, 'tcx> {
814    type NestedFilter = rustc_middle::hir::nested_filter::All;
815
816    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
817        self.genv.tcx()
818    }
819
820    fn visit_mod(&mut self, module: &'tcx hir::Mod<'tcx>, _s: Span, hir_id: hir::HirId) {
821        let old_mod = self.current_module;
822        self.current_module = hir_id.expect_owner();
823        self.push_rib(TypeNS, RibKind::Module);
824        self.push_rib(ValueNS, RibKind::Module);
825        self.push_rib(ReftNS, RibKind::Module);
826
827        self.define_items(module.item_ids);
828
829        // Flux primops and wualifiers are made globally available as if they were defined at the top of the crate
830        if hir_id == CRATE_HIR_ID {
831            self.define_flux_global_items();
832        }
833        // Other items are defined in the module they are declared in.
834        self.define_module_flux_items(hir_id.expect_owner());
835
836        self.resolve_flux_items(hir_id.expect_owner());
837        hir::intravisit::walk_mod(self, module);
838
839        self.pop_rib(ReftNS);
840        self.pop_rib(ValueNS);
841        self.pop_rib(TypeNS);
842        self.current_module = old_mod;
843    }
844
845    fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
846        let parent = self.genv.tcx().hir_get_parent_item(block.hir_id);
847
848        self.push_rib(TypeNS, RibKind::Misc);
849        self.push_rib(ValueNS, RibKind::Misc);
850        self.push_rib(ReftNS, RibKind::Misc);
851
852        let item_ids = block.stmts.iter().filter_map(|stmt| {
853            if let hir::StmtKind::Item(item_id) = &stmt.kind { Some(item_id) } else { None }
854        });
855        self.define_items(item_ids);
856        self.define_module_flux_items(parent);
857
858        self.resolve_flux_items(parent);
859        hir::intravisit::walk_block(self, block);
860
861        self.pop_rib(ReftNS);
862        self.pop_rib(ValueNS);
863        self.pop_rib(TypeNS);
864    }
865
866    fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
867        if self.genv.is_dummy(item.owner_id.def_id) {
868            return;
869        }
870        let def_id = self
871            .genv
872            .maybe_extern_id(item.owner_id.def_id)
873            .map(|def_id| OwnerId { def_id });
874
875        self.push_rib(TypeNS, RibKind::Misc);
876        self.push_rib(ValueNS, RibKind::Misc);
877
878        match item.kind {
879            ItemKind::Trait(..) => {
880                self.define_generics(def_id);
881                self.define_res_in(
882                    fhir::Res::SelfTyParam { trait_: def_id.resolved_id() },
883                    TypeNS,
884                    BindingSource::Explicit(Ident::with_dummy_span(kw::SelfUpper)),
885                );
886            }
887            ItemKind::Impl(hir::Impl { of_trait, .. }) => {
888                self.define_generics(def_id);
889                self.define_res_in(
890                    fhir::Res::SelfTyAlias {
891                        alias_to: def_id.resolved_id(),
892                        is_trait_impl: of_trait.is_some(),
893                    },
894                    TypeNS,
895                    BindingSource::Explicit(Ident::with_dummy_span(kw::SelfUpper)),
896                );
897            }
898            ItemKind::TyAlias(..) => {
899                self.define_generics(def_id);
900            }
901            ItemKind::Enum(..) => {
902                self.define_generics(def_id);
903                self.define_res_in(
904                    fhir::Res::SelfTyAlias { alias_to: def_id.resolved_id(), is_trait_impl: false },
905                    TypeNS,
906                    BindingSource::Explicit(Ident::with_dummy_span(kw::SelfUpper)),
907                );
908            }
909            ItemKind::Struct(..) => {
910                self.define_generics(def_id);
911                self.define_res_in(
912                    fhir::Res::SelfTyAlias { alias_to: def_id.resolved_id(), is_trait_impl: false },
913                    TypeNS,
914                    BindingSource::Explicit(Ident::with_dummy_span(kw::SelfUpper)),
915                );
916            }
917            ItemKind::Fn { .. } => {
918                self.define_generics(def_id);
919            }
920            _ => {}
921        }
922        if let Some(item) = self.specs.get_item(def_id.local_id()) {
923            self.resolve_item(item, def_id).collect_err(&mut self.err);
924        }
925
926        hir::intravisit::walk_item(self, item);
927
928        self.pop_rib(ValueNS);
929        self.pop_rib(TypeNS);
930    }
931
932    fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
933        let def_id = self
934            .genv
935            .maybe_extern_id(impl_item.owner_id.def_id)
936            .map(|def_id| OwnerId { def_id });
937
938        self.push_rib(TypeNS, RibKind::Misc);
939        if let Some(item) = self.specs.get_impl_item(def_id.local_id()) {
940            self.define_generics(def_id);
941            self.resolve_impl_item(item, def_id)
942                .collect_err(&mut self.err);
943        }
944        hir::intravisit::walk_impl_item(self, impl_item);
945        self.pop_rib(TypeNS);
946    }
947
948    fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
949        let def_id = self
950            .genv
951            .maybe_extern_id(trait_item.owner_id.def_id)
952            .map(|def_id| OwnerId { def_id });
953
954        self.push_rib(TypeNS, RibKind::Misc);
955        if let Some(item) = self.specs.get_trait_item(def_id.local_id()) {
956            self.define_generics(def_id);
957            self.resolve_trait_item(item, def_id)
958                .collect_err(&mut self.err);
959        }
960        hir::intravisit::walk_trait_item(self, trait_item);
961        self.pop_rib(TypeNS);
962    }
963}
964
965/// Akin to `rustc_resolve::Module` but specialized to what we support
966#[derive(Clone, Copy, Debug)]
967struct Module {
968    kind: ModuleKind,
969    def_id: DefId,
970}
971
972impl Module {
973    fn new(kind: ModuleKind, def_id: DefId) -> Self {
974        Self { kind, def_id }
975    }
976}
977
978/// Akin to `rustc_resolve::ModuleKind` but specialized to what we support
979#[derive(Clone, Copy, Debug)]
980enum ModuleKind {
981    Mod,
982    Trait,
983    Enum,
984}
985
986#[derive(Clone, Copy, PartialEq, Eq, Debug)]
987pub(crate) enum RibKind {
988    /// An rib with no special rules.
989    Misc,
990    /// A module boundary. This is a barrier for item resolution.
991    Module,
992    /// A function signature's inputs (arguments and `requires`). `@` binders are legal here.
993    FnInput,
994    /// A function signature's output (return type and `ensures`). `#` binders are legal here.
995    FnOutput,
996    /// An enum variant. `@` binders are legal here. This is a barrier scope because variants
997    /// are nested within `refined_by` params.
998    Variant,
999    /// The input position of an `Fn`-trait bound (e.g. the `T` in `FnMut(T) -> S`). `@` binders are
1000    /// legal here.
1001    FnTraitInput,
1002}
1003
1004/// The value stored for each binding in a [`Rib`]. Lookups and clash detection are keyed by the
1005/// name in the [`Rib`]'s map, so all a binding carries is where it came from, for diagnostics that
1006/// point at the previous location of a name.
1007#[derive(Clone, Copy, Debug)]
1008struct Binding {
1009    /// Where the name was brought into scope: the item, generic or param itself, or, for a
1010    /// glob-imported binding, the `use ...::*;` statement rather than the imported item's
1011    /// declaration, matching where rustc points its `E0659` notes.
1012    span: Span,
1013    res: fhir::Res<surface::NodeId>,
1014}
1015
1016/// How a name was brought into a rib. Explicit definitions (items, generics, params, single
1017/// `use`s, flux `use`s) shadow glob imports of the same name in the same namespace, regardless of
1018/// declaration order; only globs can be ambiguous with each other.
1019#[derive(Clone, Copy, Debug)]
1020enum BindingSource {
1021    /// A name written where it is defined: an item, generic, param or single `use`.
1022    Explicit(Ident),
1023    /// A glob-imported name: the imported item's ident, plus the span of the `use ...::*;`
1024    /// statement that brought it in, which is the one diagnostics cite.
1025    Glob { ident: Ident, glob_span: Span },
1026}
1027
1028impl BindingSource {
1029    fn ident(self) -> Ident {
1030        match self {
1031            BindingSource::Explicit(ident) | BindingSource::Glob { ident, .. } => ident,
1032        }
1033    }
1034
1035    /// Where the name was brought into scope.
1036    fn span(self) -> Span {
1037        match self {
1038            BindingSource::Explicit(ident) => ident.span,
1039            BindingSource::Glob { glob_span, .. } => glob_span,
1040        }
1041    }
1042}
1043
1044/// The glob-imported candidate(s) for a name, mirroring rustc's `glob_decl` slot.
1045#[derive(Clone, Copy, Debug)]
1046enum GlobBinding {
1047    Single(Binding),
1048    /// Two competing globs bound this name to different items. Frozen once set: further
1049    /// candidates never change it. We deliberately diverge from rustc here, which keeps updating
1050    /// the second span as new distinct competitors show up; with three or more globs we therefore
1051    /// report the first two rather than the first and the last.
1052    Ambiguous(Span, Span),
1053}
1054
1055/// All candidates for a single name in a single namespace, split into the two slots
1056/// (`non_glob_decl`/`glob_decl`). The non-glob slot always wins.
1057#[derive(Clone, Copy, Debug, Default)]
1058struct NameResolution {
1059    non_glob: Option<Binding>,
1060    glob: Option<GlobBinding>,
1061}
1062
1063impl NameResolution {
1064    fn from_explicit(binding: Binding) -> Self {
1065        Self { non_glob: Some(binding), glob: None }
1066    }
1067
1068    fn is_empty(&self) -> bool {
1069        self.non_glob.is_none() && self.glob.is_none()
1070    }
1071
1072    /// Define the binding. Return previously defined binding if there's a clash
1073    fn define(
1074        &mut self,
1075        source: BindingSource,
1076        res: fhir::Res<surface::NodeId>,
1077    ) -> Option<Binding> {
1078        let binding = Binding { span: source.span(), res };
1079        match source {
1080            BindingSource::Explicit(_) => {
1081                if let Some(prev) = self.non_glob {
1082                    Some(prev)
1083                } else {
1084                    self.non_glob = Some(binding);
1085                    None
1086                }
1087            }
1088            BindingSource::Glob { .. } => {
1089                self.add_glob(binding);
1090                None
1091            }
1092        }
1093    }
1094
1095    /// Fold one more glob candidate into the glob slot. A candidate resolving to the same item as
1096    /// the one already recorded (the same item reached through two different globs) is not an
1097    /// ambiguity.
1098    fn add_glob(&mut self, binding: Binding) {
1099        self.glob = Some(match self.glob {
1100            None => GlobBinding::Single(binding),
1101            Some(GlobBinding::Single(prev)) => {
1102                if prev.res == binding.res {
1103                    GlobBinding::Single(prev)
1104                } else {
1105                    GlobBinding::Ambiguous(prev.span, binding.span)
1106                }
1107            }
1108            Some(ambiguous @ GlobBinding::Ambiguous(..)) => ambiguous,
1109        });
1110    }
1111
1112    /// The resolution for `ident`, or `None` if this holds no candidate at all (in which case the
1113    /// caller should keep looking in outer scopes).
1114    fn resolve(
1115        &self,
1116        ident: Ident,
1117    ) -> Option<std::result::Result<fhir::Res<surface::NodeId>, Ambiguity>> {
1118        if let Some(binding) = self.non_glob {
1119            return Some(Ok(binding.res));
1120        }
1121        match self.glob? {
1122            GlobBinding::Single(binding) => Some(Ok(binding.res)),
1123            GlobBinding::Ambiguous(first, second) => {
1124                Some(Err(Ambiguity { span: ident.span, name: ident.name, first, second }))
1125            }
1126        }
1127    }
1128}
1129
1130impl FromIterator<Binding> for NameResolution {
1131    /// Fold a stream of *glob* candidates. Used to merge a module's children, where two entries
1132    /// for the same name can only come from an ambiguous glob re-export.
1133    fn from_iter<T: IntoIterator<Item = Binding>>(iter: T) -> Self {
1134        let mut resolution = Self::default();
1135        for binding in iter {
1136            resolution.add_glob(binding);
1137        }
1138        resolution
1139    }
1140}
1141
1142#[derive(Debug)]
1143struct Rib {
1144    kind: RibKind,
1145    bindings: UnordMap<Symbol, NameResolution>,
1146}
1147
1148impl Rib {
1149    fn new(kind: RibKind) -> Self {
1150        Self { kind, bindings: Default::default() }
1151    }
1152}
1153
1154fn module_children(tcx: TyCtxt<'_>, def_id: DefId) -> &[ModChild] {
1155    #[expect(clippy::disallowed_methods, reason = "modules cannot have extern specs")]
1156    if let Some(local_id) = def_id.as_local() {
1157        tcx.module_children_local(local_id)
1158    } else {
1159        tcx.module_children(def_id)
1160    }
1161}
1162
1163/// Iterator over module children visible form `curr_mod`
1164fn visible_module_children(
1165    tcx: TyCtxt<'_>,
1166    module_id: DefId,
1167    curr_mod: DefId,
1168) -> impl Iterator<Item = &ModChild> {
1169    module_children(tcx, module_id)
1170        .iter()
1171        .filter(move |child| child.vis.is_accessible_from(curr_mod, tcx))
1172}
1173
1174/// Return true if the item has a `#[prelude_import]` annotation
1175fn is_prelude_import(tcx: TyCtxt, item: &hir::Item) -> bool {
1176    tcx.hir_attrs(item.hir_id())
1177        .iter()
1178        .any(|attr| attr.path_matches(&[sym::prelude_import]))
1179}
1180
1181/// Abstraction over a "segment" so we can use [`CrateResolver::resolve_path_with_ribs`] with paths
1182/// from different sources  (e.g., [`surface::PathSegment`], [`surface::ExprPathSegment`])
1183trait Segment: std::fmt::Debug {
1184    fn record_segment_res(
1185        resolver: &mut CrateResolver,
1186        segment: &Self,
1187        res: fhir::Res<surface::NodeId>,
1188    );
1189    fn ident(&self) -> Ident;
1190
1191    fn format_iter<'a>(segments: impl IntoIterator<Item = &'a Self>) -> String
1192    where
1193        Self: Sized + 'a,
1194    {
1195        segments.into_iter().map(|s| s.ident()).join("::")
1196    }
1197}
1198
1199impl Segment for surface::PathSegment {
1200    fn record_segment_res(
1201        resolver: &mut CrateResolver,
1202        segment: &Self,
1203        res: fhir::Res<surface::NodeId>,
1204    ) {
1205        resolver
1206            .output
1207            .path_res_map
1208            .insert(segment.node_id, fhir::PartialRes::new(res));
1209    }
1210
1211    fn ident(&self) -> Ident {
1212        self.ident
1213    }
1214}
1215
1216impl Segment for surface::ExprPathSegment {
1217    fn record_segment_res(
1218        resolver: &mut CrateResolver,
1219        segment: &Self,
1220        res: fhir::Res<surface::NodeId>,
1221    ) {
1222        resolver
1223            .output
1224            .path_res_map
1225            .insert(segment.node_id, fhir::PartialRes::new(res));
1226    }
1227
1228    fn ident(&self) -> Ident {
1229        self.ident
1230    }
1231}
1232
1233impl Segment for Ident {
1234    fn record_segment_res(
1235        _resolver: &mut CrateResolver,
1236        _segment: &Self,
1237        _res: fhir::Res<surface::NodeId>,
1238    ) {
1239    }
1240
1241    fn ident(&self) -> Ident {
1242        *self
1243    }
1244}
1245
1246impl Segment for hir::PathSegment<'_> {
1247    fn record_segment_res(
1248        _resolver: &mut CrateResolver,
1249        _segment: &Self,
1250        _res: fhir::Res<surface::NodeId>,
1251    ) {
1252    }
1253
1254    fn ident(&self) -> Ident {
1255        self.ident
1256    }
1257}
1258
1259struct ItemResolver<'a, 'genv, 'tcx> {
1260    resolver: &'a mut CrateResolver<'genv, 'tcx>,
1261    errors: Errors<'genv>,
1262    item_id: MaybeExternId<OwnerId>,
1263}
1264
1265impl<'a, 'genv, 'tcx> ItemResolver<'a, 'genv, 'tcx> {
1266    fn run(
1267        resolver: &'a mut CrateResolver<'genv, 'tcx>,
1268        item_id: MaybeExternId<OwnerId>,
1269        f: impl FnOnce(&mut ItemResolver),
1270    ) -> Result {
1271        let mut item_resolver = ItemResolver::new(resolver, item_id);
1272        f(&mut item_resolver);
1273        item_resolver.errors.into_result()
1274    }
1275
1276    fn new(resolver: &'a mut CrateResolver<'genv, 'tcx>, item_id: MaybeExternId<OwnerId>) -> Self {
1277        let errors = Errors::new(resolver.genv.sess());
1278        Self { resolver, errors, item_id }
1279    }
1280
1281    fn resolve_path_in(&mut self, ns: Namespace, path: &surface::Path) {
1282        match self.resolver.resolve_path_with_ribs(&path.segments, ns) {
1283            Ok(partial_res) => {
1284                self.resolver
1285                    .output
1286                    .path_res_map
1287                    .insert(path.node_id, partial_res);
1288            }
1289            Err(ResolveError::NotFound) => self.emit_unresolved_path(path, ns),
1290            Err(ResolveError::Ambiguous(ambiguity)) => self.emit_ambiguity_err(ambiguity),
1291        }
1292    }
1293
1294    fn resolve_attrs(&mut self, node_id: surface::NodeId, attrs: &[surface::Attr]) {
1295        for attr in attrs {
1296            match attr {
1297                surface::Attr::Qualifiers(names) => self.resolve_qualifiers(node_id, names),
1298                surface::Attr::Reveal(names) => self.resolve_reveals(node_id, names),
1299                surface::Attr::AssumeParametric(names) => {
1300                    self.resolve_parametric_params(node_id, names);
1301                }
1302                _ => {}
1303            }
1304        }
1305    }
1306
1307    fn resolve_parametric_params(&mut self, node_id: surface::NodeId, names: &[Ident]) {
1308        let tcx = self.resolver.genv.tcx();
1309        let generics = tcx.generics_of(self.item_id.resolved_id());
1310        let param_map: UnordMap<Symbol, DefId> = (0..generics.count())
1311            .map(|i| generics.param_at(i, tcx))
1312            .map(|p| (p.name, p.def_id))
1313            .collect();
1314        let mut params = Vec::with_capacity(names.len());
1315        for name in names {
1316            if let Some(&def_id) = param_map.get(&name.name) {
1317                params.push(def_id);
1318            } else {
1319                self.errors
1320                    .emit(errors::UnknownParametricParam::new(name.span));
1321            }
1322        }
1323        self.resolver
1324            .output
1325            .parametric_param_res_map
1326            .insert(node_id, params);
1327    }
1328
1329    fn resolve_qualifiers(&mut self, node_id: surface::NodeId, qual_names: &[Ident]) {
1330        let mut qualifiers = Vec::with_capacity(qual_names.len());
1331        for qual in qual_names {
1332            if let Some(def_id) = self.resolver.qualifiers.get(&qual.name) {
1333                qualifiers.push(*def_id);
1334            } else {
1335                self.errors.emit(errors::UnknownQualifier::new(qual.span));
1336            }
1337        }
1338        self.resolver
1339            .output
1340            .qualifier_res_map
1341            .insert(node_id, qualifiers);
1342    }
1343
1344    fn resolve_reveals(&mut self, item_id: surface::NodeId, reveal_names: &[Ident]) {
1345        let mut reveals = Vec::with_capacity(reveal_names.len());
1346        for reveal in reveal_names {
1347            match self.resolver.resolve_ident_with_ribs(*reveal, ReftNS) {
1348                Ok(fhir::Res::GlobalFunc(kind)) => {
1349                    if let Some(def_id) = kind.def_id() {
1350                        reveals.push(def_id);
1351                    } else {
1352                        self.errors
1353                            .emit(errors::UnknownRevealDefinition::new(reveal.span));
1354                    }
1355                }
1356                Ok(_) | Err(ResolveError::NotFound) => {
1357                    self.errors
1358                        .emit(errors::UnknownRevealDefinition::new(reveal.span));
1359                }
1360                Err(ResolveError::Ambiguous(ambiguity)) => self.emit_ambiguity_err(ambiguity),
1361            }
1362        }
1363        self.resolver.output.reveal_res_map.insert(item_id, reveals);
1364    }
1365
1366    fn emit_ambiguity_err(&mut self, ambiguity: Ambiguity) {
1367        self.errors.emit(errors::AmbiguousName::new(ambiguity));
1368    }
1369
1370    fn emit_unresolved_path(&mut self, path: &surface::Path, ns: Namespace) {
1371        self.errors.emit(errors::UnresolvedName {
1372            span: path.span,
1373            name: Segment::format_iter(&path.segments),
1374            kind: ns.descr(),
1375        });
1376    }
1377}
1378
1379impl surface::visit::Visitor for ItemResolver<'_, '_, '_> {
1380    fn visit_item(&mut self, item: &surface::Item) {
1381        self.resolve_attrs(item.node_id, &item.attrs);
1382        surface::visit::walk_item(self, item);
1383    }
1384
1385    fn visit_trait_item(&mut self, item: &surface::TraitItemFn) {
1386        self.resolve_attrs(item.node_id, &item.attrs);
1387        surface::visit::walk_trait_item(self, item);
1388    }
1389
1390    fn visit_impl_item(&mut self, item: &surface::ImplItemFn) {
1391        self.resolve_attrs(item.node_id, &item.attrs);
1392        surface::visit::walk_impl_item(self, item);
1393    }
1394
1395    fn visit_trait(&mut self, trait_: &surface::Trait) {
1396        let mut definitions = DefinitionMap::default();
1397        for assoc_reft in &trait_.assoc_refinements {
1398            let _ = definitions.define(assoc_reft.name).emit(&self.errors);
1399        }
1400        surface::visit::walk_trait(self, trait_);
1401    }
1402
1403    fn visit_impl(&mut self, impl_: &surface::Impl) {
1404        let mut definitions = DefinitionMap::default();
1405        for assoc_reft in &impl_.assoc_refinements {
1406            let _ = definitions.define(assoc_reft.name).emit(&self.errors);
1407        }
1408        surface::visit::walk_impl(self, impl_);
1409    }
1410
1411    fn visit_generic_arg(&mut self, arg: &surface::GenericArg) {
1412        if let surface::GenericArgKind::Type(ty) = &arg.kind
1413            && let Some(path) = ty.is_potential_const_arg()
1414        {
1415            // We parse const arguments as path types as we cannot distinguish them during
1416            // parsing. We try to resolve that ambiguity by attempting resolution in both the
1417            // type and value namespaces. If we resolved the path in the value namespace, we
1418            // transform it into a generic const argument.
1419            // A name that is *ambiguous* in a namespace still counts as present there: rerouting
1420            // to the other namespace would hide the ambiguity instead of reporting it. The real
1421            // diagnostic comes from `resolve_path_in` below.
1422            let check_ns = |ns| {
1423                !matches!(
1424                    self.resolver.resolve_ident_with_ribs(path.last().ident, ns),
1425                    Err(ResolveError::NotFound)
1426                )
1427            };
1428
1429            if !check_ns(TypeNS) && check_ns(ValueNS) {
1430                self.resolve_path_in(ValueNS, path);
1431                return;
1432            }
1433        }
1434        surface::visit::walk_generic_arg(self, arg);
1435    }
1436
1437    fn visit_const_arg(&mut self, const_arg: &surface::ConstArg) {
1438        if let surface::ConstArgKind::Path(path) = &const_arg.kind {
1439            self.resolve_path_in(ValueNS, path);
1440            surface::visit::walk_path(self, path);
1441        }
1442    }
1443
1444    fn visit_path(&mut self, path: &surface::Path) {
1445        self.resolve_path_in(TypeNS, path);
1446        surface::visit::walk_path(self, path);
1447    }
1448}
1449
1450/// The [`Namespace::TypeNS`] prelude: builtin Rust types (`bool`, `i32`, `str`, ...) together with
1451/// the sort-only primitive sorts (`int`, `real`, `Set`, `Map`, `ptr`). Sorts and types share the
1452/// type namespace; `bool`/`char`/`str` are resolved as [`fhir::Res::PrimTy`] and their sort is
1453/// derived in `conv_sort_path` (see [`fhir::PrimSort`]).
1454fn builtin_types_rib() -> Rib {
1455    use flux_middle::fhir::PrimSort;
1456    let sorts = PrimSort::ALL.into_iter().map(|prim| {
1457        let ident = Ident::with_dummy_span(prim.name());
1458        (
1459            ident.name,
1460            NameResolution::from_explicit(Binding {
1461                span: ident.span,
1462                res: fhir::Res::PrimSort(prim),
1463            }),
1464        )
1465    });
1466    let types = PrimTy::ALL.into_iter().map(|pty| {
1467        let ident = Ident::with_dummy_span(pty.name());
1468        (
1469            ident.name,
1470            NameResolution::from_explicit(Binding {
1471                span: ident.span,
1472                res: fhir::Res::PrimTy(pty),
1473            }),
1474        )
1475    });
1476
1477    // Types go after such that they override sorts with the same name. Note this collects into a
1478    // map, so it is a plain last-one-wins overwrite, not the shadowing/ambiguity fold used for
1479    // user-written definitions.
1480    let bindings = sorts.chain(types).collect();
1481    Rib { kind: RibKind::Misc, bindings }
1482}
1483
1484/// The [`Namespace::ReftNS`] prelude: theory functions and `cast`.
1485fn theory_funcs_rib() -> Rib {
1486    let mut rib = Rib::new(RibKind::Misc);
1487    rib.bindings
1488        .extend_unord(flux_middle::THEORY_FUNCS.items().map(|(_, itf)| {
1489            let ident = Ident::with_dummy_span(itf.name);
1490            let res = fhir::Res::GlobalFunc(fhir::SpecFuncKind::Thy(itf.itf));
1491            (ident.name, NameResolution::from_explicit(Binding { span: ident.span, res }))
1492        }));
1493    let cast_ident = Ident::with_dummy_span(sym::cast);
1494    rib.bindings.insert(
1495        cast_ident.name,
1496        NameResolution::from_explicit(Binding {
1497            span: cast_ident.span,
1498            res: fhir::Res::GlobalFunc(fhir::SpecFuncKind::Cast),
1499        }),
1500    );
1501    rib
1502}
1503
1504fn mk_crate_mapping(tcx: TyCtxt) -> UnordMap<Symbol, DefId> {
1505    let mut map = UnordMap::default();
1506    for cnum in tcx.crates(()) {
1507        let name = tcx.crate_name(*cnum);
1508        if let Some(extern_crate) = tcx.extern_crate(*cnum)
1509            && extern_crate.is_direct()
1510        {
1511            map.insert(name, cnum.as_def_id());
1512        }
1513    }
1514    map
1515}
1516
1517mod errors {
1518    use flux_errors::E0999;
1519    use flux_macros::Diagnostic;
1520    use rustc_span::{Span, Symbol};
1521
1522    /// A name that could not be resolved. `kind` is the user-facing description of what was being
1523    /// looked for (`"type"`, `"value"`, `"sort"`, ...); it is passed explicitly by each call site
1524    /// because it no longer matches the resolution [`Namespace`](flux_middle::fhir::Namespace)
1525    /// (e.g. sorts are resolved in the type namespace).
1526    #[derive(Diagnostic)]
1527    #[diag(desugar_unresolved_name, code = E0999)]
1528    pub(crate) struct UnresolvedName {
1529        #[primary_span]
1530        #[label]
1531        pub span: Span,
1532        pub kind: &'static str,
1533        pub name: String,
1534    }
1535
1536    /// An import path (`flux::use foo::bar::baz`) that could not be resolved. Unlike
1537    /// [`UnresolvedName`], this always reports the full requested path in the message and
1538    /// explains, via `reason`, what specifically went wrong at the failing segment (not found,
1539    /// or found but not a module).
1540    #[derive(Diagnostic)]
1541    #[diag(desugar_unresolved_import, code = E0999)]
1542    pub(crate) struct UnresolvedImport {
1543        #[primary_span]
1544        #[label]
1545        pub span: Span,
1546        pub name: String,
1547        pub reason: String,
1548    }
1549
1550    #[derive(Diagnostic)]
1551    #[diag(desugar_unknown_qualifier, code = E0999)]
1552    pub(super) struct UnknownQualifier {
1553        #[primary_span]
1554        span: Span,
1555    }
1556
1557    impl UnknownQualifier {
1558        pub(super) fn new(span: Span) -> Self {
1559            Self { span }
1560        }
1561    }
1562
1563    #[derive(Diagnostic)]
1564    #[diag(desugar_unknown_reveal_definition, code = E0999)]
1565    pub(super) struct UnknownRevealDefinition {
1566        #[primary_span]
1567        span: Span,
1568    }
1569
1570    impl UnknownRevealDefinition {
1571        pub(super) fn new(span: Span) -> Self {
1572            Self { span }
1573        }
1574    }
1575
1576    #[derive(Diagnostic)]
1577    #[diag(desugar_unknown_parametric_param, code = E0999)]
1578    pub(super) struct UnknownParametricParam {
1579        #[primary_span]
1580        span: Span,
1581    }
1582
1583    impl UnknownParametricParam {
1584        pub(super) fn new(span: Span) -> Self {
1585            Self { span }
1586        }
1587    }
1588
1589    #[derive(Diagnostic)]
1590    #[diag(desugar_duplicate_definition, code = E0999)]
1591    pub(super) struct DuplicateDefinition {
1592        #[primary_span]
1593        #[label]
1594        pub span: Span,
1595        #[label(desugar_previous_definition)]
1596        pub previous_definition: Span,
1597        pub name: Symbol,
1598    }
1599
1600    /// A name bound to two different items by two competing glob imports, reported at the first
1601    /// use of the name (imports themselves are never an error). Mirrors rustc's `E0659`.
1602    #[derive(Diagnostic)]
1603    #[diag(desugar_ambiguous_name, code = E0999)]
1604    #[note]
1605    pub(super) struct AmbiguousName {
1606        #[primary_span]
1607        #[label]
1608        span: Span,
1609        name: Symbol,
1610        #[label(desugar_first_candidate)]
1611        first: Span,
1612        #[label(desugar_second_candidate)]
1613        second: Option<Span>,
1614    }
1615
1616    impl AmbiguousName {
1617        pub(super) fn new(ambiguity: super::Ambiguity) -> Self {
1618            let super::Ambiguity { span, name, first, second } = ambiguity;
1619            // Both candidates can come from the same glob statement, when it imports a module
1620            // that is itself ambiguously glob re-exporting the name. Pointing a second, identical
1621            // label at it reads as a bug, so drop it.
1622            Self { span, name, first, second: (first != second).then_some(second) }
1623        }
1624    }
1625
1626    #[derive(Diagnostic)]
1627    #[diag(desugar_duplicate_param, code = E0999)]
1628    pub(super) struct DuplicateParam {
1629        #[primary_span]
1630        #[label]
1631        pub span: Span,
1632        pub name: Symbol,
1633        #[label(desugar_first_use)]
1634        pub first_use: Span,
1635    }
1636}