rune/query/
query.rs

1#[cfg(feature = "emit")]
2use core::mem::take;
3use core::ops::{Deref, DerefMut};
4
5use ::rust_alloc::rc::Rc;
6use ::rust_alloc::sync::Arc;
7
8use crate::alloc::borrow::Cow;
9use crate::alloc::prelude::*;
10use crate::alloc::{self, BTreeMap, HashSet, VecDeque};
11use crate::alloc::{hash_map, HashMap};
12use crate::ast;
13use crate::ast::{Span, Spanned};
14use crate::compile::context::ContextMeta;
15use crate::compile::{
16    self, ir, meta, CompileVisitor, Doc, DynLocation, ErrorKind, ImportStep, ItemId, ItemMeta,
17    Located, Location, MetaError, ModId, ModMeta, Names, Pool, Prelude, SourceLoader, SourceMeta,
18    UnitBuilder, Visibility, WithSpan,
19};
20use crate::grammar::{Ignore, Node, Stream};
21use crate::hir;
22use crate::indexing::{self, FunctionAst, Indexed, Items};
23use crate::item::ComponentRef;
24use crate::item::IntoComponent;
25use crate::macros::Storage;
26use crate::parse::{NonZeroId, Resolve};
27#[cfg(feature = "doc")]
28use crate::runtime::Call;
29use crate::runtime::ConstValue;
30use crate::shared::{Consts, Gen};
31use crate::{Context, Diagnostics, Hash, Item, ItemBuf, Options, SourceId, Sources};
32
33use super::{
34    Build, BuildEntry, BuiltInMacro, ConstFn, DeferEntry, ExpandedMacro, GenericsParameters, Named,
35    Named2, Named2Kind, Used,
36};
37
38enum ContextMatch<'this, 'm> {
39    Context(&'m ContextMeta, Hash),
40    Meta(&'this meta::Meta),
41    None,
42}
43
44/// The permitted number of import recursions when constructing a path.
45const IMPORT_RECURSION_LIMIT: usize = 128;
46
47#[derive(Default)]
48pub(crate) struct QueryInner<'arena> {
49    /// Resolved meta about every single item during a compilation.
50    meta: HashMap<(ItemId, Hash), meta::Meta>,
51    /// Build queue.
52    pub(crate) queue: VecDeque<BuildEntry>,
53    /// Set of used items.
54    used: HashSet<ItemId>,
55    /// Indexed items that can be queried for, which will queue up for them to
56    /// be compiled.
57    indexed: BTreeMap<ItemId, Vec<indexing::Entry>>,
58    /// Compiled constant functions.
59    const_fns: HashMap<ItemId, Rc<ConstFn<'arena>>>,
60    /// Indexed constant values.
61    constants: HashMap<Hash, ConstValue>,
62    /// The result of internally resolved macros.
63    internal_macros: HashMap<NonZeroId, Arc<BuiltInMacro>>,
64    /// Expanded macros.
65    expanded_macros: HashMap<NonZeroId, ExpandedMacro>,
66    /// Associated between `id` and `Item`. Use to look up items through
67    /// `item_for` with an opaque id.
68    ///
69    /// These items are associated with AST elements, and encodoes the item path
70    /// that the AST element was indexed.
71    pub(crate) items: HashMap<ItemId, ItemMeta>,
72    /// All available names.
73    names: Names,
74    /// Queue of impl items to process.
75    pub(crate) defer_queue: VecDeque<DeferEntry>,
76}
77
78impl QueryInner<'_> {
79    /// Get a constant value but only from the dynamic query system.
80    pub(crate) fn get_const_value(&self, hash: Hash) -> Option<&ConstValue> {
81        self.constants.get(&hash)
82    }
83}
84
85pub(crate) struct QuerySource<'a, 'arena> {
86    query: Query<'a, 'arena>,
87    source_id: SourceId,
88}
89
90impl<'a, 'arena> Deref for QuerySource<'a, 'arena> {
91    type Target = Query<'a, 'arena>;
92
93    #[inline]
94    fn deref(&self) -> &Self::Target {
95        &self.query
96    }
97}
98
99impl DerefMut for QuerySource<'_, '_> {
100    #[inline]
101    fn deref_mut(&mut self) -> &mut Self::Target {
102        &mut self.query
103    }
104}
105
106impl<'a> Ignore<'a> for QuerySource<'_, '_> {
107    #[inline]
108    fn error(&mut self, error: compile::Error) -> alloc::Result<()> {
109        self.query.diagnostics.error(self.source_id, error)
110    }
111
112    #[inline]
113    fn ignore(&mut self, _: Node<'a>) -> compile::Result<()> {
114        Ok(())
115    }
116}
117
118/// Query system of the rune compiler.
119///
120/// The basic mode of operation here is that you ask for an item, and the query
121/// engine gives you the metadata for that item while queueing up any tasks that
122/// need to be run to actually build that item and anything associated with it.
123///
124/// Note that this type has a lot of `pub(crate)` items. This is intentional.
125/// Many components need to perform complex borrowing out of this type, meaning
126/// its fields need to be visible (see the [resolve_context!] macro).
127pub(crate) struct Query<'a, 'arena> {
128    /// The current unit being built.
129    pub(crate) unit: &'a mut UnitBuilder,
130    /// The prelude in effect.
131    prelude: &'a Prelude,
132    /// Arena used for constant contexts.
133    pub(crate) const_arena: &'arena hir::Arena,
134    /// Cache of constants that have been expanded.
135    pub(crate) consts: &'a mut Consts,
136    /// Storage associated with the query.
137    pub(crate) storage: &'a mut Storage,
138    /// Sources available.
139    pub(crate) sources: &'a mut Sources,
140    /// Pool of allocates items and modules.
141    pub(crate) pool: &'a mut Pool,
142    /// Visitor for the compiler meta.
143    pub(crate) visitor: &'a mut dyn CompileVisitor,
144    /// Compilation warnings.
145    pub(crate) diagnostics: &'a mut Diagnostics,
146    /// Source loader.
147    pub(crate) source_loader: &'a mut dyn SourceLoader,
148    /// Build options.
149    pub(crate) options: &'a Options,
150    /// Shared id generator.
151    pub(crate) gen: &'a Gen,
152    /// Native context.
153    pub(crate) context: &'a Context,
154    /// Inner state of the query engine.
155    pub(crate) inner: &'a mut QueryInner<'arena>,
156}
157
158impl<'a, 'arena> Query<'a, 'arena> {
159    /// Construct a new compilation context.
160    pub(crate) fn new(
161        unit: &'a mut UnitBuilder,
162        prelude: &'a Prelude,
163        const_arena: &'arena hir::Arena,
164        consts: &'a mut Consts,
165        storage: &'a mut Storage,
166        sources: &'a mut Sources,
167        pool: &'a mut Pool,
168        visitor: &'a mut dyn CompileVisitor,
169        diagnostics: &'a mut Diagnostics,
170        source_loader: &'a mut dyn SourceLoader,
171        options: &'a Options,
172        gen: &'a Gen,
173        context: &'a Context,
174        inner: &'a mut QueryInner<'arena>,
175    ) -> Self {
176        Self {
177            unit,
178            prelude,
179            const_arena,
180            consts,
181            storage,
182            sources,
183            pool,
184            visitor,
185            diagnostics,
186            source_loader,
187            options,
188            gen,
189            context,
190            inner,
191        }
192    }
193
194    /// Reborrow the query engine from a reference to `self`.
195    pub(crate) fn borrow(&mut self) -> Query<'_, 'arena> {
196        Query {
197            unit: self.unit,
198            prelude: self.prelude,
199            const_arena: self.const_arena,
200            consts: self.consts,
201            storage: self.storage,
202            pool: self.pool,
203            sources: self.sources,
204            visitor: self.visitor,
205            diagnostics: self.diagnostics,
206            source_loader: self.source_loader,
207            options: self.options,
208            gen: self.gen,
209            context: self.context,
210            inner: self.inner,
211        }
212    }
213
214    /// Get a query with source.
215    pub(crate) fn with_source_id(&mut self, source_id: SourceId) -> QuerySource<'_, 'arena> {
216        QuerySource {
217            query: self.borrow(),
218            source_id,
219        }
220    }
221
222    /// Test if the given meta item id is used.
223    pub(crate) fn is_used(&self, item_meta: &ItemMeta) -> bool {
224        self.inner.used.contains(&item_meta.item)
225    }
226
227    /// Set the given meta item as used.
228    pub(crate) fn set_used(&mut self, item_meta: &ItemMeta) -> alloc::Result<()> {
229        self.inner.used.try_insert(item_meta.item)?;
230        Ok(())
231    }
232
233    /// Insert a new macro to build.
234    pub(crate) fn insert_new_macro(
235        &mut self,
236        expand_macro: impl FnOnce(NonZeroId) -> alloc::Result<DeferEntry>,
237    ) -> alloc::Result<NonZeroId> {
238        let id = self.gen.next();
239        self.inner.defer_queue.try_push_back(expand_macro(id)?)?;
240        Ok(id)
241    }
242
243    /// Get the next impl item in queue to process.
244    pub(crate) fn next_defer_entry(&mut self) -> Option<DeferEntry> {
245        self.inner.defer_queue.pop_front()
246    }
247
248    /// Get the next build entry from the build queue associated with the query
249    /// engine.
250    pub(crate) fn next_build_entry(&mut self) -> Option<BuildEntry> {
251        self.inner.queue.pop_front()
252    }
253
254    // Pick private metadata to compile for the item.
255    fn select_context_meta<'this, 'm>(
256        &'this self,
257        item: ItemId,
258        metas: impl Iterator<Item = &'m ContextMeta> + Clone,
259        parameters: &GenericsParameters,
260    ) -> Result<ContextMatch<'this, 'm>, rust_alloc::boxed::Box<ErrorKind>> {
261        #[derive(Debug, PartialEq, Eq, Clone, Copy)]
262        enum Kind {
263            None,
264            Type,
265            Function,
266            AssociatedFunction,
267        }
268
269        /// Determine how the collection of generic parameters applies to the
270        /// returned context meta.
271        fn determine_kind<'m>(metas: impl Iterator<Item = &'m ContextMeta>) -> Option<Kind> {
272            let mut kind = Kind::None;
273
274            for meta in metas {
275                let alt = match &meta.kind {
276                    meta::Kind::Enum { .. }
277                    | meta::Kind::Struct { .. }
278                    | meta::Kind::Type { .. } => Kind::Type,
279                    meta::Kind::Function {
280                        associated: None, ..
281                    } => Kind::Function,
282                    meta::Kind::Function {
283                        associated: Some(..),
284                        ..
285                    } => Kind::AssociatedFunction,
286                    _ => {
287                        continue;
288                    }
289                };
290
291                if matches!(kind, Kind::None) {
292                    kind = alt;
293                    continue;
294                }
295
296                if kind != alt {
297                    return None;
298                }
299            }
300
301            Some(kind)
302        }
303
304        fn build_parameters(kind: Kind, p: &GenericsParameters) -> Option<Hash> {
305            let hash = match (kind, p.trailing, p.parameters) {
306                (_, 0, _) => Hash::EMPTY,
307                (Kind::Type, 1, [Some(ty), None]) => Hash::EMPTY.with_type_parameters(ty),
308                (Kind::Function, 1, [Some(f), None]) => Hash::EMPTY.with_function_parameters(f),
309                (Kind::AssociatedFunction, 1, [Some(f), None]) => {
310                    Hash::EMPTY.with_function_parameters(f)
311                }
312                (Kind::AssociatedFunction, 2, [Some(ty), f]) => Hash::EMPTY
313                    .with_type_parameters(ty)
314                    .with_function_parameters(f.unwrap_or(Hash::EMPTY)),
315                _ => {
316                    return None;
317                }
318            };
319
320            Some(hash)
321        }
322
323        if let Some(parameters) =
324            determine_kind(metas.clone()).and_then(|kind| build_parameters(kind, parameters))
325        {
326            if let Some(meta) = self.get_meta(item, parameters) {
327                return Ok(ContextMatch::Meta(meta));
328            }
329
330            // If there is a single item matching the specified generic hash, pick
331            // it.
332            let mut it = metas
333                .clone()
334                .filter(|i| !matches!(i.kind, meta::Kind::Macro | meta::Kind::Module))
335                .filter(|i| i.kind.as_parameters() == parameters);
336
337            if let Some(meta) = it.next() {
338                if it.next().is_none() {
339                    return Ok(ContextMatch::Context(meta, parameters));
340                }
341            } else {
342                return Ok(ContextMatch::None);
343            }
344        }
345
346        if metas.clone().next().is_none() {
347            return Ok(ContextMatch::None);
348        }
349
350        Err(rust_alloc::boxed::Box::new(
351            ErrorKind::AmbiguousContextItem {
352                item: self.pool.item(item).try_to_owned()?,
353                #[cfg(feature = "emit")]
354                infos: metas
355                    .map(|i| i.info())
356                    .try_collect::<alloc::Result<_>>()??,
357            },
358        ))
359    }
360
361    /// Access the meta for the given language item.
362    #[tracing::instrument(skip_all, fields(item = ?self.pool.item(item), parameters))]
363    pub(crate) fn try_lookup_meta(
364        &mut self,
365        location: &dyn Located,
366        item: ItemId,
367        parameters: &GenericsParameters,
368    ) -> compile::Result<Option<meta::Meta>> {
369        tracing::trace!("looking up meta");
370
371        if parameters.is_empty() {
372            if let Some(meta) = self.query_meta(location.as_spanned(), item, Default::default())? {
373                tracing::trace!(?meta, "found in query");
374
375                self.visitor
376                    .visit_meta(location, meta.as_meta_ref(self.pool))
377                    .with_span(location.as_spanned())?;
378                return Ok(Some(meta));
379            }
380        }
381
382        let Some(metas) = self.context.lookup_meta(self.pool.item(item)) else {
383            return Ok(None);
384        };
385
386        let (meta, parameters) = match self
387            .select_context_meta(item, metas, parameters)
388            .with_span(location.as_spanned())?
389        {
390            ContextMatch::None => return Ok(None),
391            ContextMatch::Meta(meta) => return Ok(Some(meta.try_clone()?)),
392            ContextMatch::Context(meta, parameters) => (meta, parameters),
393        };
394
395        let Some(item) = &meta.item else {
396            return Err(compile::Error::new(
397                location.as_spanned(),
398                ErrorKind::MissingItemHash { hash: meta.hash },
399            ));
400        };
401
402        let item = self.pool.alloc_item(item)?;
403
404        let meta = meta::Meta {
405            context: true,
406            hash: meta.hash,
407            item_meta: self.context_item_meta(item, None),
408            kind: meta.kind.try_clone()?,
409            source: None,
410            parameters,
411        };
412
413        self.insert_meta(meta.try_clone()?)
414            .with_span(location.as_spanned())?;
415
416        tracing::trace!(?meta, "Found in context");
417
418        self.visitor
419            .visit_meta(location, meta.as_meta_ref(self.pool))
420            .with_span(location.as_spanned())?;
421
422        Ok(Some(meta))
423    }
424
425    /// Access the meta for the given language item.
426    pub(crate) fn lookup_meta(
427        &mut self,
428        location: &dyn Located,
429        item: ItemId,
430        parameters: impl AsRef<GenericsParameters>,
431    ) -> compile::Result<meta::Meta> {
432        let parameters = parameters.as_ref();
433
434        if let Some(meta) = self.try_lookup_meta(location, item, parameters)? {
435            return Ok(meta);
436        }
437
438        let kind = if !parameters.parameters.is_empty() {
439            ErrorKind::MissingItemParameters {
440                item: self.pool.item(item).try_to_owned()?,
441                parameters: parameters.parameters,
442            }
443        } else {
444            ErrorKind::MissingItem {
445                item: self.pool.item(item).try_to_owned()?,
446            }
447        };
448
449        Err(compile::Error::new(location.as_spanned(), kind))
450    }
451
452    pub(crate) fn lookup_deprecation(&self, hash: Hash) -> Option<&str> {
453        self.context.lookup_deprecation(hash)
454    }
455
456    /// Insert module and associated metadata.
457    pub(crate) fn insert_mod(
458        &mut self,
459        items: &Items,
460        location: &dyn Located,
461        parent: ModId,
462        visibility: Visibility,
463        docs: &[Doc],
464    ) -> compile::Result<(ModId, ItemId)> {
465        let item = self.pool.alloc_item(items.item())?;
466
467        let module = self.pool.alloc_module(ModMeta {
468            #[cfg(feature = "emit")]
469            location: location.location(),
470            item,
471            visibility,
472            parent: Some(parent),
473        })?;
474
475        let item_meta =
476            self.insert_new_item_with(item, module, None, location, visibility, docs)?;
477
478        self.index_and_build(indexing::Entry {
479            item_meta,
480            indexed: Indexed::Module,
481        })?;
482
483        Ok((module, item))
484    }
485
486    /// Insert module and associated metadata.
487    pub(crate) fn insert_root_mod(
488        &mut self,
489        source_id: SourceId,
490        span: Span,
491    ) -> compile::Result<(ItemId, ModId)> {
492        let location = Location::new(source_id, span);
493
494        let module = self.pool.alloc_module(ModMeta {
495            #[cfg(feature = "emit")]
496            location,
497            item: ItemId::ROOT,
498            visibility: Visibility::Public,
499            parent: None,
500        })?;
501
502        self.inner.items.try_insert(
503            ItemId::ROOT,
504            ItemMeta {
505                location,
506                item: ItemId::ROOT,
507                visibility: Visibility::Public,
508                module,
509                impl_item: None,
510            },
511        )?;
512
513        self.insert_name(ItemId::ROOT).with_span(span)?;
514        Ok((ItemId::ROOT, module))
515    }
516
517    /// Inserts an item that *has* to be unique, else cause an error.
518    ///
519    /// This are not indexed and does not generate an ID, they're only visible
520    /// in reverse lookup.
521    pub(crate) fn insert_new_item(
522        &mut self,
523        items: &Items,
524        module: ModId,
525        impl_item: Option<ItemId>,
526        location: &dyn Located,
527        visibility: Visibility,
528        docs: &[Doc],
529    ) -> compile::Result<ItemMeta> {
530        let item = self.pool.alloc_item(items.item())?;
531        self.insert_new_item_with(item, module, impl_item, location, visibility, docs)
532    }
533
534    /// Insert the given compile meta.
535    pub(crate) fn insert_meta(&mut self, meta: meta::Meta) -> Result<&ItemMeta, MetaError> {
536        self.visitor.register_meta(meta.as_meta_ref(self.pool))?;
537
538        let meta = match self
539            .inner
540            .meta
541            .entry((meta.item_meta.item, meta.parameters))
542        {
543            hash_map::Entry::Occupied(e) => {
544                return Err(MetaError::new(
545                    compile::error::MetaErrorKind::MetaConflict {
546                        current: meta.info(self.pool)?,
547                        existing: e.get().info(self.pool)?,
548                        parameters: meta.parameters,
549                    },
550                ));
551            }
552            hash_map::Entry::Vacant(e) => e.try_insert(meta)?,
553        };
554
555        Ok(&meta.item_meta)
556    }
557
558    /// Insert a new item with the given newly allocated identifier and complete
559    /// `Item`.
560    fn insert_new_item_with(
561        &mut self,
562        item: ItemId,
563        module: ModId,
564        impl_item: Option<ItemId>,
565        location: &dyn Located,
566        visibility: Visibility,
567        docs: &[Doc],
568    ) -> compile::Result<ItemMeta> {
569        let location = location.location();
570
571        // Emit documentation comments for the given item.
572        if !docs.is_empty() {
573            let cx = resolve_context!(self);
574
575            for doc in docs {
576                self.visitor
577                    .visit_doc_comment(
578                        &DynLocation::new(location.source_id, &doc.span),
579                        self.pool.item(item),
580                        self.pool.item_type_hash(item),
581                        doc.doc_string.resolve(cx)?.as_ref(),
582                    )
583                    .with_span(location)?;
584            }
585        }
586
587        let item_meta = ItemMeta {
588            location,
589            item,
590            module,
591            visibility,
592            impl_item,
593        };
594
595        self.inner.items.try_insert(item, item_meta)?;
596        Ok(item_meta)
597    }
598
599    /// Insert a new expanded internal macro.
600    pub(crate) fn insert_new_builtin_macro(
601        &mut self,
602        internal_macro: BuiltInMacro,
603    ) -> compile::Result<NonZeroId> {
604        let id = self.gen.next();
605        self.inner
606            .internal_macros
607            .try_insert(id, Arc::new(internal_macro))?;
608        Ok(id)
609    }
610
611    /// Insert a new expanded macro.
612    pub(crate) fn insert_expanded_macro(
613        &mut self,
614        id: NonZeroId,
615        expanded: ExpandedMacro,
616    ) -> compile::Result<()> {
617        self.inner.expanded_macros.try_insert(id, expanded)?;
618        Ok(())
619    }
620
621    /// Get an expanded macro.
622    pub(crate) fn take_expanded_macro(&mut self, id: NonZeroId) -> Option<ExpandedMacro> {
623        self.inner.expanded_macros.remove(&id)
624    }
625
626    /// Get the item for the given identifier.
627    pub(crate) fn item_for(&self, what: &'static str, id: ItemId) -> anyhow::Result<ItemMeta> {
628        let Some(item_meta) = self.inner.items.get(&id) else {
629            let m = try_format!(
630                "missing item meta for `{what}` at {} with id {id}",
631                self.pool.item(id)
632            );
633            return Err(anyhow::Error::msg(m));
634        };
635
636        Ok(*item_meta)
637    }
638
639    /// Get the built-in macro matching the given ast.
640    pub(crate) fn builtin_macro_for(&self, id: NonZeroId) -> anyhow::Result<Arc<BuiltInMacro>> {
641        let Some(internal_macro) = self.inner.internal_macros.get(&id) else {
642            let m = try_format!("missing built-in macro for id {id}");
643            return Err(anyhow::Error::msg(m));
644        };
645
646        Ok(internal_macro.clone())
647    }
648
649    /// Get the constant function associated with the opaque.
650    pub(crate) fn const_fn_for(&self, id: ItemId) -> anyhow::Result<Rc<ConstFn<'a>>> {
651        let Some(const_fn) = self.inner.const_fns.get(&id) else {
652            let m = try_format!(
653                "missing constant function {} for id {id}",
654                self.pool.item(id)
655            );
656            return Err(anyhow::Error::msg(m));
657        };
658
659        Ok(const_fn.clone())
660    }
661
662    /// Index the given entry. It is not allowed to overwrite other entries.
663    #[tracing::instrument(skip_all)]
664    pub(crate) fn index(&mut self, entry: indexing::Entry) -> compile::Result<()> {
665        tracing::trace!(item = ?self.pool.item(entry.item_meta.item));
666
667        self.insert_name(entry.item_meta.item)
668            .with_span(entry.item_meta.location.span)?;
669
670        self.inner
671            .indexed
672            .entry(entry.item_meta.item)
673            .or_try_default()?
674            .try_push(entry)?;
675
676        Ok(())
677    }
678
679    /// Same as `index`, but also queues the indexed entry up for building.
680    #[tracing::instrument(skip_all)]
681    pub(crate) fn index_and_build(&mut self, entry: indexing::Entry) -> compile::Result<()> {
682        self.set_used(&entry.item_meta)?;
683
684        self.inner.queue.try_push_back(BuildEntry {
685            item_meta: entry.item_meta,
686            build: Build::Query,
687        })?;
688
689        self.index(entry)?;
690        Ok(())
691    }
692
693    /// Index a constant expression.
694    #[tracing::instrument(skip_all)]
695    pub(crate) fn index_const_expr(
696        &mut self,
697        item_meta: ItemMeta,
698        const_expr: indexing::ConstExpr,
699    ) -> compile::Result<()> {
700        tracing::trace!(item = ?self.pool.item(item_meta.item));
701
702        self.index(indexing::Entry {
703            item_meta,
704            indexed: Indexed::ConstExpr(const_expr),
705        })?;
706
707        Ok(())
708    }
709
710    /// Index a constant expression.
711    #[tracing::instrument(skip_all)]
712    pub(crate) fn index_const_block(
713        &mut self,
714        item_meta: ItemMeta,
715        block: indexing::ConstBlock,
716    ) -> compile::Result<()> {
717        tracing::trace!(item = ?self.pool.item(item_meta.item));
718
719        self.index(indexing::Entry {
720            item_meta,
721            indexed: Indexed::ConstBlock(block),
722        })?;
723
724        Ok(())
725    }
726
727    /// Index a constant function.
728    #[tracing::instrument(skip_all)]
729    pub(crate) fn index_const_fn(
730        &mut self,
731        item_meta: ItemMeta,
732        const_fn: indexing::ConstFn,
733    ) -> compile::Result<()> {
734        tracing::trace!(item = ?self.pool.item(item_meta.item));
735
736        self.index(indexing::Entry {
737            item_meta,
738            indexed: Indexed::ConstFn(const_fn),
739        })?;
740
741        Ok(())
742    }
743
744    /// Add a new enum item.
745    #[tracing::instrument(skip_all)]
746    pub(crate) fn index_enum(&mut self, item_meta: ItemMeta) -> compile::Result<()> {
747        tracing::trace!(item = ?self.pool.item(item_meta.item));
748
749        self.index(indexing::Entry {
750            item_meta,
751            indexed: Indexed::Enum,
752        })?;
753
754        Ok(())
755    }
756
757    /// Add a new struct item that can be queried.
758    #[tracing::instrument(skip_all)]
759    pub(crate) fn index_struct(
760        &mut self,
761        item_meta: ItemMeta,
762        st: indexing::Struct,
763    ) -> compile::Result<()> {
764        tracing::trace!(item = ?self.pool.item(item_meta.item));
765
766        self.index(indexing::Entry {
767            item_meta,
768            indexed: Indexed::Struct(st),
769        })?;
770
771        Ok(())
772    }
773
774    /// Add a new variant item that can be queried.
775    #[tracing::instrument(skip_all)]
776    pub(crate) fn index_variant(
777        &mut self,
778        item_meta: ItemMeta,
779        variant: indexing::Variant,
780    ) -> compile::Result<()> {
781        tracing::trace!(item = ?self.pool.item(item_meta.item));
782
783        self.index(indexing::Entry {
784            item_meta,
785            indexed: Indexed::Variant(variant),
786        })?;
787
788        Ok(())
789    }
790
791    /// Index meta immediately.
792    #[tracing::instrument(skip_all)]
793    pub(crate) fn index_meta(
794        &mut self,
795        span: &dyn Spanned,
796        item_meta: ItemMeta,
797        kind: meta::Kind,
798    ) -> compile::Result<()> {
799        tracing::trace!(item = ?self.pool.item(item_meta.item));
800
801        let source = SourceMeta {
802            location: item_meta.location,
803            path: self
804                .sources
805                .path(item_meta.location.source_id)
806                .map(|p| p.try_into())
807                .transpose()?,
808        };
809
810        let meta = meta::Meta {
811            context: false,
812            hash: self.pool.item_type_hash(item_meta.item),
813            item_meta,
814            kind,
815            source: Some(source),
816            parameters: Hash::EMPTY,
817        };
818
819        self.unit.insert_meta(span, &meta, self.pool, self.inner)?;
820        self.insert_meta(meta).with_span(span)?;
821        Ok(())
822    }
823
824    /// Remove and queue up unused entries for building.
825    ///
826    /// Returns boolean indicating if any unused entries were queued up.
827    #[tracing::instrument(skip_all)]
828    pub(crate) fn queue_unused_entries(
829        &mut self,
830        errors: &mut Vec<(SourceId, compile::Error)>,
831    ) -> alloc::Result<bool> {
832        tracing::trace!("Queue unused");
833
834        let unused = self
835            .inner
836            .indexed
837            .values()
838            .flat_map(|entries| entries.iter())
839            .map(|e| (e.item_meta.location, e.item_meta.item))
840            .try_collect::<Vec<_>>()?;
841
842        if unused.is_empty() {
843            return Ok(true);
844        }
845
846        for (location, item) in unused {
847            if let Err(error) = self.query_indexed_meta(&location, item, Used::Unused) {
848                errors.try_push((location.source_id, error))?;
849            }
850        }
851
852        Ok(false)
853    }
854
855    /// Explicitly look for meta with the given item and hash.
856    pub(crate) fn get_meta(&self, item: ItemId, hash: Hash) -> Option<&meta::Meta> {
857        self.inner.meta.get(&(item, hash))
858    }
859
860    /// Query for the given meta by looking up the reverse of the specified
861    /// item.
862    #[tracing::instrument(skip(self, span, item), fields(item = ?self.pool.item(item)))]
863    pub(crate) fn query_meta(
864        &mut self,
865        span: &dyn Spanned,
866        item: ItemId,
867        used: Used,
868    ) -> compile::Result<Option<meta::Meta>> {
869        if let Some(meta) = self.inner.meta.get(&(item, Hash::EMPTY)) {
870            tracing::trace!(item = ?item, meta = ?meta, "cached");
871            // Ensure that the given item is not indexed, cause if it is
872            // `queue_unused_entries` might end up spinning indefinitely since
873            // it will never be exhausted.
874            debug_assert!(!self.inner.indexed.contains_key(&item));
875            return Ok(Some(meta.try_clone()?));
876        }
877
878        self.query_indexed_meta(span, item, used)
879    }
880
881    /// Only try and query for meta among items which have been indexed.
882    #[tracing::instrument(skip_all, fields(item = ?self.pool.item(item)))]
883    fn query_indexed_meta(
884        &mut self,
885        span: &dyn Spanned,
886        item: ItemId,
887        used: Used,
888    ) -> compile::Result<Option<meta::Meta>> {
889        tracing::trace!("query indexed meta");
890
891        if let Some(entry) = self.remove_indexed(span, item)? {
892            let meta = self.build_indexed_entry(span, entry, used)?;
893            self.unit.insert_meta(span, &meta, self.pool, self.inner)?;
894            self.insert_meta(meta.try_clone()?).with_span(span)?;
895            tracing::trace!(item = ?item, meta = ?meta, "build");
896            return Ok(Some(meta));
897        }
898
899        Ok(None)
900    }
901
902    /// Perform a default path conversion.
903    pub(crate) fn convert_path<'ast>(
904        &mut self,
905        path: &'ast ast::Path,
906    ) -> compile::Result<Named<'ast>> {
907        self.convert_path_with(path, false, Used::Used, Used::Used)
908    }
909
910    /// Perform a path conversion with custom configuration.
911    #[tracing::instrument(skip(self, path))]
912    pub(crate) fn convert_path_with<'ast>(
913        &mut self,
914        path: &'ast ast::Path,
915        deny_self_type: bool,
916        import_used: Used,
917        used: Used,
918    ) -> compile::Result<Named<'ast>> {
919        tracing::trace!("converting path");
920
921        let Some(&ItemMeta {
922            module,
923            item,
924            impl_item,
925            ..
926        }) = self.inner.items.get(&path.id)
927        else {
928            return Err(compile::Error::msg(
929                path,
930                try_format!("Missing query path for id {}", path.id),
931            ));
932        };
933
934        let mut in_self_type = false;
935
936        let item = match (&path.global, &path.first) {
937            (Some(..), ast::PathSegment::Ident(ident)) => self
938                .pool
939                .alloc_item(ItemBuf::with_crate(ident.resolve(resolve_context!(self))?)?)?,
940            (Some(span), _) => {
941                return Err(compile::Error::new(span, ErrorKind::UnsupportedGlobal));
942            }
943            (None, segment) => match segment {
944                ast::PathSegment::Ident(ident) => {
945                    self.convert_initial_path(module, item, ident, used)?
946                }
947                ast::PathSegment::Super(..) => {
948                    let Some(segment) = self
949                        .pool
950                        .try_map_alloc(self.pool.module(module).item, Item::parent)?
951                    else {
952                        return Err(compile::Error::new(segment, ErrorKind::UnsupportedSuper));
953                    };
954
955                    segment
956                }
957                ast::PathSegment::SelfType(..) => {
958                    let impl_item = match impl_item {
959                        Some(impl_item) if !deny_self_type => impl_item,
960                        _ => {
961                            return Err(compile::Error::new(
962                                segment.span(),
963                                ErrorKind::UnsupportedSelfType,
964                            ));
965                        }
966                    };
967
968                    let Some(impl_item) = self.inner.items.get(&impl_item) else {
969                        return Err(compile::Error::msg(
970                            segment.span(),
971                            "Can't use `Self` due to unexpanded impl item",
972                        ));
973                    };
974
975                    in_self_type = true;
976                    impl_item.item
977                }
978                ast::PathSegment::SelfValue(..) => self.pool.module(module).item,
979                ast::PathSegment::Crate(..) => ItemId::ROOT,
980                ast::PathSegment::Generics(..) => {
981                    return Err(compile::Error::new(
982                        segment.span(),
983                        ErrorKind::UnsupportedGenerics,
984                    ));
985                }
986            },
987        };
988
989        let mut item = self.pool.item(item).try_to_owned()?;
990        let mut trailing = 0;
991        let mut parameters: [Option<(&dyn Spanned, _)>; 2] = [None, None];
992
993        let mut it = path.rest.iter();
994        let mut parameters_it = parameters.iter_mut();
995
996        for (_, segment) in it.by_ref() {
997            match segment {
998                ast::PathSegment::Ident(ident) => {
999                    item.push(ident.resolve(resolve_context!(self))?)?;
1000                }
1001                ast::PathSegment::Super(span) => {
1002                    if in_self_type {
1003                        return Err(compile::Error::new(
1004                            span,
1005                            ErrorKind::UnsupportedSuperInSelfType,
1006                        ));
1007                    }
1008
1009                    if !item.pop() {
1010                        return Err(compile::Error::new(segment, ErrorKind::UnsupportedSuper));
1011                    }
1012                }
1013                ast::PathSegment::Generics(arguments) => {
1014                    let Some(p) = parameters_it.next() else {
1015                        return Err(compile::Error::new(segment, ErrorKind::UnsupportedGenerics));
1016                    };
1017
1018                    trailing += 1;
1019                    *p = Some((segment, arguments));
1020                    break;
1021                }
1022                _ => {
1023                    return Err(compile::Error::new(
1024                        segment.span(),
1025                        ErrorKind::ExpectedLeadingPathSegment,
1026                    ));
1027                }
1028            }
1029        }
1030
1031        // Consume remaining generics, possibly interleaved with identifiers.
1032        while let Some((_, segment)) = it.next() {
1033            let ast::PathSegment::Ident(ident) = segment else {
1034                return Err(compile::Error::new(
1035                    segment.span(),
1036                    ErrorKind::UnsupportedAfterGeneric,
1037                ));
1038            };
1039
1040            trailing += 1;
1041            item.push(ident.resolve(resolve_context!(self))?)?;
1042
1043            let Some(p) = parameters_it.next() else {
1044                return Err(compile::Error::new(segment, ErrorKind::UnsupportedGenerics));
1045            };
1046
1047            let Some(ast::PathSegment::Generics(arguments)) = it.clone().next().map(|(_, p)| p)
1048            else {
1049                continue;
1050            };
1051
1052            *p = Some((segment, arguments));
1053            it.next();
1054        }
1055
1056        let item = self.pool.alloc_item(item)?;
1057
1058        if let Some(new) = self.import(path, module, item, import_used, used)? {
1059            return Ok(Named {
1060                module,
1061                item: new,
1062                trailing,
1063                parameters,
1064            });
1065        }
1066
1067        Ok(Named {
1068            module,
1069            item,
1070            trailing,
1071            parameters,
1072        })
1073    }
1074
1075    /// Perform a default path conversion.
1076    pub(crate) fn convert_path2<'ast>(
1077        &mut self,
1078        p: &mut Stream<'ast>,
1079    ) -> compile::Result<Named2<'ast>> {
1080        self.convert_path2_with(p, false, Used::Used, Used::Used)
1081    }
1082
1083    /// Perform a path conversion with custom configuration.
1084    #[tracing::instrument(skip(self, p))]
1085    pub(crate) fn convert_path2_with<'ast>(
1086        &mut self,
1087        p: &mut Stream<'ast>,
1088        deny_self_type: bool,
1089        import_used: Used,
1090        used: Used,
1091    ) -> compile::Result<Named2<'ast>> {
1092        use ast::Kind::*;
1093
1094        let IndexedPath(id) = p.kind() else {
1095            return Err(p.expected(Path));
1096        };
1097
1098        tracing::trace!("converting path");
1099
1100        let Some(&ItemMeta {
1101            module,
1102            item,
1103            impl_item,
1104            ..
1105        }) = self.inner.items.get(&id)
1106        else {
1107            return Err(compile::Error::msg(
1108                &*p,
1109                try_format!("missing query path for id {id}"),
1110            ));
1111        };
1112
1113        let mut trailing = 0;
1114        let mut parameters = [None, None];
1115
1116        let (item, kind) = 'out: {
1117            match p.kinds() {
1118                Some([K![ident]]) => {
1119                    let ast = p.ast::<ast::Ident>()?;
1120                    let item = self.convert_initial_path(module, item, &ast, used)?;
1121                    let kind = Named2Kind::Ident(ast);
1122                    break 'out (item, kind);
1123                }
1124                Some([K![self]]) => {
1125                    let ast = p.ast::<ast::SelfValue>()?;
1126                    let item = self.pool.module(module).item;
1127                    let kind = Named2Kind::SelfValue(ast);
1128                    break 'out (item, kind);
1129                }
1130                _ => {}
1131            }
1132
1133            let item = self.path_full(
1134                p,
1135                deny_self_type,
1136                used,
1137                module,
1138                item,
1139                impl_item,
1140                &mut trailing,
1141                &mut parameters,
1142            )?;
1143
1144            (item, Named2Kind::Full)
1145        };
1146
1147        let item = self
1148            .import(&*p, module, item, import_used, used)?
1149            .unwrap_or(item);
1150
1151        Ok(Named2 {
1152            module,
1153            kind,
1154            item,
1155            trailing,
1156            parameters,
1157        })
1158    }
1159
1160    /// Parse a full path.
1161    fn path_full<'ast>(
1162        &mut self,
1163        p: &mut Stream<'ast>,
1164        deny_self_type: bool,
1165        used: Used,
1166        module: ModId,
1167        item: ItemId,
1168        impl_item: Option<ItemId>,
1169        trailing: &mut usize,
1170        parameters: &mut [Option<Node<'ast>>],
1171    ) -> compile::Result<ItemId> {
1172        use ast::Kind::*;
1173
1174        let mut in_self_type = false;
1175
1176        let is_global = p.eat(K![::]).span();
1177        let first = p.pump()?;
1178
1179        let (item, mut supports_generics) = match (is_global, first.kind()) {
1180            (Some(..), K![ident]) => {
1181                let first = first.ast::<ast::Ident>()?;
1182                let first = first.resolve(resolve_context!(self))?;
1183                let item = self.pool.alloc_item(ItemBuf::with_crate(first)?)?;
1184                (item, true)
1185            }
1186            (Some(node), _) => {
1187                return Err(compile::Error::new(node, ErrorKind::UnsupportedGlobal));
1188            }
1189            (None, K![ident]) => {
1190                let first = first.ast::<ast::Ident>()?;
1191                let item = self.convert_initial_path(module, item, &first, used)?;
1192                (item, true)
1193            }
1194            (None, K![super]) => {
1195                let Some(item) = self
1196                    .pool
1197                    .try_map_alloc(self.pool.module(module).item, crate::Item::parent)?
1198                else {
1199                    return Err(compile::Error::new(first, ErrorKind::UnsupportedSuper));
1200                };
1201
1202                (item, false)
1203            }
1204            (None, K![Self]) => {
1205                let impl_item = match impl_item {
1206                    Some(impl_item) if !deny_self_type => impl_item,
1207                    _ => {
1208                        return Err(compile::Error::new(first, ErrorKind::UnsupportedSelfType));
1209                    }
1210                };
1211
1212                let Some(impl_item) = self.inner.items.get(&impl_item) else {
1213                    return Err(compile::Error::msg(
1214                        first,
1215                        "Can't use `Self` due to unexpanded impl item",
1216                    ));
1217                };
1218
1219                in_self_type = true;
1220                (impl_item.item, false)
1221            }
1222            (None, K![self]) => {
1223                let item = self.pool.module(module).item;
1224                (item, false)
1225            }
1226            (None, K![crate]) => (ItemId::ROOT, false),
1227            (_, PathGenerics) => {
1228                return Err(compile::Error::new(first, ErrorKind::UnsupportedGenerics));
1229            }
1230            _ => {
1231                return Err(first.expected(Path));
1232            }
1233        };
1234
1235        let mut item = self.pool.item(item).try_to_owned()?;
1236        let mut it = parameters.iter_mut();
1237
1238        while !p.is_eof() {
1239            p.expect(K![::])?;
1240            let node = p.pump()?;
1241
1242            match node.kind() {
1243                K![ident] => {
1244                    let ident = node.ast::<ast::Ident>()?;
1245                    item.push(ident.resolve(resolve_context!(self))?)?;
1246                    supports_generics = true;
1247                }
1248                K![super] => {
1249                    if in_self_type {
1250                        return Err(compile::Error::new(
1251                            node,
1252                            ErrorKind::UnsupportedSuperInSelfType,
1253                        ));
1254                    }
1255
1256                    if !item.pop() {
1257                        return Err(compile::Error::new(node, ErrorKind::UnsupportedSuper));
1258                    }
1259                }
1260                PathGenerics if supports_generics => {
1261                    let Some(out) = it.next() else {
1262                        return Err(compile::Error::new(node, ErrorKind::UnsupportedGenerics));
1263                    };
1264
1265                    *trailing += 1;
1266                    *out = Some(node);
1267                    break;
1268                }
1269                _ => {
1270                    return Err(compile::Error::new(
1271                        node,
1272                        ErrorKind::ExpectedLeadingPathSegment,
1273                    ));
1274                }
1275            }
1276        }
1277
1278        while !p.is_eof() {
1279            p.expect(K![::])?;
1280            let ident = p.pump()?.ast::<ast::Ident>()?;
1281            item.push(ident.resolve(resolve_context!(self))?)?;
1282            *trailing += 1;
1283
1284            let Some(node) = p.next() else {
1285                break;
1286            };
1287
1288            let Some(out) = it.next() else {
1289                return Err(compile::Error::new(node, ErrorKind::UnsupportedGenerics));
1290            };
1291
1292            *out = Some(p.expect(PathGenerics)?);
1293        }
1294
1295        let item = self.pool.alloc_item(item)?;
1296        Ok(item)
1297    }
1298
1299    /// Declare a new import.
1300    #[tracing::instrument(skip_all)]
1301    pub(crate) fn insert_import(
1302        &mut self,
1303        location: &dyn Located,
1304        module: ModId,
1305        visibility: Visibility,
1306        at: &Item,
1307        target: &Item,
1308        alias: Option<ast::Ident>,
1309        wildcard: bool,
1310    ) -> compile::Result<()> {
1311        tracing::trace!(at = ?at, target = ?target);
1312
1313        let alias = match alias {
1314            Some(alias) => Some(alias.resolve(resolve_context!(self))?),
1315            None => None,
1316        };
1317
1318        let Some(last) = alias
1319            .as_ref()
1320            .map(IntoComponent::as_component_ref)
1321            .or_else(|| target.last())
1322        else {
1323            return Err(compile::Error::new(
1324                location.as_spanned(),
1325                ErrorKind::LastUseComponent,
1326            ));
1327        };
1328
1329        let item = self.pool.alloc_item(at.extended(last)?)?;
1330        let target = self.pool.alloc_item(target)?;
1331
1332        let entry = meta::Import {
1333            location: location.location(),
1334            target,
1335            module,
1336        };
1337
1338        let item_meta = self.insert_new_item_with(item, module, None, location, visibility, &[])?;
1339
1340        // toplevel public uses are re-exported.
1341        if item_meta.is_public(self.pool) {
1342            self.inner.used.try_insert(item_meta.item)?;
1343
1344            self.inner.queue.try_push_back(BuildEntry {
1345                item_meta,
1346                build: Build::ReExport,
1347            })?;
1348        }
1349
1350        self.index(indexing::Entry {
1351            item_meta,
1352            indexed: Indexed::Import(indexing::Import { wildcard, entry }),
1353        })?;
1354
1355        Ok(())
1356    }
1357
1358    /// Check if unit contains the given name by prefix.
1359    pub(crate) fn contains_prefix(&self, item: &Item) -> alloc::Result<bool> {
1360        self.inner.names.contains_prefix(item)
1361    }
1362
1363    /// Iterate over known child components of the given name.
1364    pub(crate) fn iter_components<'it, I>(
1365        &'it self,
1366        iter: I,
1367    ) -> alloc::Result<impl Iterator<Item = ComponentRef<'it>> + 'it>
1368    where
1369        I: 'it + IntoIterator,
1370        I::Item: IntoComponent,
1371    {
1372        self.inner.names.iter_components(iter)
1373    }
1374
1375    /// Get the given import by name.
1376    #[tracing::instrument(skip(self, span, module))]
1377    pub(crate) fn import(
1378        &mut self,
1379        span: &dyn Spanned,
1380        mut module: ModId,
1381        item: ItemId,
1382        import_used: Used,
1383        used: Used,
1384    ) -> compile::Result<Option<ItemId>> {
1385        let mut visited = HashSet::<ItemId>::new();
1386        let mut path = Vec::new();
1387        let mut item = self.pool.item(item).try_to_owned()?;
1388        let mut any_matched = false;
1389
1390        let mut count = 0usize;
1391
1392        'outer: loop {
1393            if count > IMPORT_RECURSION_LIMIT {
1394                return Err(compile::Error::new(
1395                    span,
1396                    ErrorKind::ImportRecursionLimit { count, path },
1397                ));
1398            }
1399
1400            count += 1;
1401
1402            let mut cur = ItemBuf::new();
1403            let mut it = item.iter();
1404
1405            while let Some(c) = it.next() {
1406                cur.push(c)?;
1407
1408                let cur = self.pool.alloc_item(&cur)?;
1409
1410                let update = self.import_step(
1411                    span,
1412                    module,
1413                    cur,
1414                    used,
1415                    #[cfg(feature = "emit")]
1416                    &mut path,
1417                )?;
1418
1419                let Some(FoundImportStep { item_meta, import }) = update else {
1420                    continue;
1421                };
1422
1423                // Imports are *always* used once they pass this step.
1424                if let Used::Used = import_used {
1425                    self.set_used(&item_meta)?;
1426                }
1427
1428                path.try_push(ImportStep {
1429                    location: import.location,
1430                    item: self.pool.item(import.target).try_to_owned()?,
1431                })?;
1432
1433                if !visited.try_insert(self.pool.alloc_item(&item)?)? {
1434                    return Err(compile::Error::new(
1435                        span,
1436                        ErrorKind::ImportCycle {
1437                            #[cfg(feature = "emit")]
1438                            path,
1439                        },
1440                    ));
1441                }
1442
1443                module = import.module;
1444                item = self.pool.item(import.target).join(it)?;
1445                any_matched = true;
1446                continue 'outer;
1447            }
1448
1449            break;
1450        }
1451
1452        if any_matched {
1453            return Ok(Some(self.pool.alloc_item(item)?));
1454        }
1455
1456        Ok(None)
1457    }
1458
1459    /// Inner import implementation that doesn't walk the imported name.
1460    #[tracing::instrument(skip(self, span, module, path))]
1461    fn import_step(
1462        &mut self,
1463        span: &dyn Spanned,
1464        module: ModId,
1465        item: ItemId,
1466        used: Used,
1467        #[cfg(feature = "emit")] path: &mut Vec<ImportStep>,
1468    ) -> compile::Result<Option<FoundImportStep>> {
1469        // already resolved query.
1470        if let Some(meta) = self.inner.meta.get(&(item, Hash::EMPTY)) {
1471            return Ok(match meta.kind {
1472                meta::Kind::Import(import) => Some(FoundImportStep {
1473                    item_meta: meta.item_meta,
1474                    import,
1475                }),
1476                _ => None,
1477            });
1478        }
1479
1480        if let Some(metas) = self.context.lookup_meta(self.pool.item(item)) {
1481            for m in metas {
1482                if let meta::Kind::Alias(alias) = &m.kind {
1483                    let target = self.pool.alloc_item(&alias.to)?;
1484
1485                    let import = meta::Import {
1486                        location: Default::default(),
1487                        target,
1488                        module,
1489                    };
1490
1491                    let meta = meta::Meta {
1492                        context: true,
1493                        hash: self.pool.item_type_hash(item),
1494                        item_meta: self.context_item_meta(item, None),
1495                        kind: meta::Kind::Import(import),
1496                        source: None,
1497                        parameters: Hash::EMPTY,
1498                    };
1499
1500                    let item_meta = self.insert_meta(meta).with_span(span)?;
1501
1502                    return Ok(Some(FoundImportStep {
1503                        item_meta: *item_meta,
1504                        import,
1505                    }));
1506                }
1507            }
1508        }
1509
1510        // resolve query.
1511        let Some(entry) = self.remove_indexed(span, item)? else {
1512            return Ok(None);
1513        };
1514
1515        self.check_access_to(
1516            span,
1517            module,
1518            item,
1519            entry.item_meta.module,
1520            #[cfg(feature = "emit")]
1521            entry.item_meta.location,
1522            entry.item_meta.visibility,
1523            #[cfg(feature = "emit")]
1524            path,
1525        )?;
1526
1527        let import = match entry.indexed {
1528            Indexed::Import(import) => import.entry,
1529            indexed => {
1530                self.import_indexed(span, entry.item_meta, indexed, used)?;
1531                return Ok(None);
1532            }
1533        };
1534
1535        let meta = meta::Meta {
1536            context: false,
1537            hash: self.pool.item_type_hash(entry.item_meta.item),
1538            item_meta: entry.item_meta,
1539            kind: meta::Kind::Import(import),
1540            source: None,
1541            parameters: Hash::EMPTY,
1542        };
1543
1544        let item_meta = self.insert_meta(meta).with_span(span)?;
1545
1546        Ok(Some(FoundImportStep {
1547            item_meta: *item_meta,
1548            import,
1549        }))
1550    }
1551
1552    fn context_item_meta(&self, item: ItemId, impl_item: Option<ItemId>) -> ItemMeta {
1553        ItemMeta {
1554            location: Default::default(),
1555            item,
1556            visibility: Default::default(),
1557            module: Default::default(),
1558            impl_item,
1559        }
1560    }
1561
1562    /// Build a single, indexed entry and return its metadata.
1563    fn build_indexed_entry(
1564        &mut self,
1565        span: &dyn Spanned,
1566        entry: indexing::Entry,
1567        used: Used,
1568    ) -> compile::Result<meta::Meta> {
1569        #[cfg(feature = "doc")]
1570        fn to_doc_names(
1571            sources: &Sources,
1572            source_id: SourceId,
1573            args: &[Span],
1574        ) -> alloc::Result<Box<[meta::DocArgument]>> {
1575            let mut out = Vec::try_with_capacity(args.len())?;
1576
1577            for (n, span) in args.iter().enumerate() {
1578                let name = match sources.source(source_id, *span) {
1579                    Some(name) => meta::DocName::Name(name.try_into()?),
1580                    None => meta::DocName::Index(n),
1581                };
1582
1583                out.try_push(meta::DocArgument {
1584                    name,
1585                    base: Hash::EMPTY,
1586                    generics: Box::default(),
1587                })?;
1588            }
1589
1590            Box::try_from(out)
1591        }
1592
1593        let indexing::Entry { item_meta, indexed } = entry;
1594
1595        if let Used::Used = used {
1596            self.inner.used.try_insert(item_meta.item)?;
1597        }
1598
1599        let kind = match indexed {
1600            Indexed::Enum => meta::Kind::Enum {
1601                parameters: Hash::EMPTY,
1602            },
1603            Indexed::Variant(variant) => {
1604                let enum_ = self.item_for("variant", variant.enum_id).with_span(span)?;
1605
1606                // Ensure that the enum is being built and marked as used.
1607                let Some(enum_meta) = self.query_meta(span, enum_.item, Default::default())? else {
1608                    return Err(compile::Error::msg(
1609                        span,
1610                        try_format!("Missing enum by {:?}", variant.enum_id),
1611                    ));
1612                };
1613
1614                meta::Kind::Struct {
1615                    fields: variant.fields,
1616                    constructor: None,
1617                    parameters: Hash::EMPTY,
1618                    enum_hash: enum_meta.hash,
1619                }
1620            }
1621            Indexed::Struct(st) => meta::Kind::Struct {
1622                fields: st.fields,
1623                constructor: None,
1624                parameters: Hash::EMPTY,
1625                enum_hash: Hash::EMPTY,
1626            },
1627            Indexed::Function(f) => {
1628                let kind = meta::Kind::Function {
1629                    associated: match (f.is_instance, &f.ast) {
1630                        (true, FunctionAst::Item(_, name)) => {
1631                            let name: Cow<str> =
1632                                Cow::Owned(name.resolve(resolve_context!(self))?.try_into()?);
1633                            Some(meta::AssociatedKind::Instance(name))
1634                        }
1635                        (true, FunctionAst::Node(_, Some(name))) => {
1636                            let name: Cow<str> =
1637                                Cow::Owned(name.resolve(resolve_context!(self))?.try_into()?);
1638                            Some(meta::AssociatedKind::Instance(name))
1639                        }
1640                        _ => None,
1641                    },
1642                    trait_hash: None,
1643                    is_test: f.is_test,
1644                    is_bench: f.is_bench,
1645                    signature: meta::Signature {
1646                        #[cfg(feature = "doc")]
1647                        is_async: matches!(f.call, Call::Async | Call::Stream),
1648                        #[cfg(feature = "doc")]
1649                        arguments: Some(to_doc_names(
1650                            self.sources,
1651                            item_meta.location.source_id,
1652                            &f.args,
1653                        )?),
1654                        #[cfg(feature = "doc")]
1655                        return_type: meta::DocType::empty(),
1656                    },
1657                    parameters: Hash::EMPTY,
1658                    #[cfg(feature = "doc")]
1659                    container: {
1660                        match f.impl_item {
1661                            Some(item) => {
1662                                let Some(impl_item) = self.inner.items.get(&item) else {
1663                                    return Err(compile::Error::msg(
1664                                        item_meta.location.span,
1665                                        "missing impl item",
1666                                    ));
1667                                };
1668
1669                                debug_assert_eq!(impl_item.item, item);
1670                                Some(self.pool.item_type_hash(impl_item.item))
1671                            }
1672                            None => None,
1673                        }
1674                    },
1675                    #[cfg(feature = "doc")]
1676                    parameter_types: Vec::new(),
1677                };
1678
1679                self.inner.queue.try_push_back(BuildEntry {
1680                    item_meta,
1681                    build: Build::Function(f),
1682                })?;
1683
1684                kind
1685            }
1686            Indexed::ConstExpr(c) => {
1687                let ir = {
1688                    let mut hir_ctx = crate::hir::Ctxt::with_const(
1689                        self.const_arena,
1690                        self.borrow(),
1691                        item_meta.location.source_id,
1692                    )?;
1693
1694                    let hir = match c {
1695                        indexing::ConstExpr::Ast(ast) => {
1696                            crate::hir::lowering::expr(&mut hir_ctx, &ast)?
1697                        }
1698                        indexing::ConstExpr::Node(node) => {
1699                            node.parse(|p| crate::hir::lowering2::expr(&mut hir_ctx, p))?
1700                        }
1701                    };
1702
1703                    let mut cx = ir::Ctxt {
1704                        source_id: item_meta.location.source_id,
1705                        q: self.borrow(),
1706                    };
1707                    ir::compiler::expr(&hir, &mut cx)?
1708                };
1709
1710                let mut const_compiler = ir::Interpreter {
1711                    budget: ir::Budget::new(1_000_000),
1712                    scopes: ir::Scopes::new()?,
1713                    module: item_meta.module,
1714                    item: item_meta.item,
1715                    q: self.borrow(),
1716                };
1717
1718                let const_value = const_compiler.eval_const(&ir, used)?;
1719
1720                let hash = self.pool.item_type_hash(item_meta.item);
1721                self.inner.constants.try_insert(hash, const_value)?;
1722
1723                if used.is_unused() {
1724                    self.inner.queue.try_push_back(BuildEntry {
1725                        item_meta,
1726                        build: Build::Unused,
1727                    })?;
1728                }
1729
1730                meta::Kind::Const
1731            }
1732            Indexed::ConstBlock(c) => {
1733                let ir = {
1734                    let mut hir_ctx = crate::hir::Ctxt::with_const(
1735                        self.const_arena,
1736                        self.borrow(),
1737                        item_meta.location.source_id,
1738                    )?;
1739
1740                    let hir = match &c {
1741                        indexing::ConstBlock::Ast(ast) => {
1742                            crate::hir::lowering::block(&mut hir_ctx, None, ast)?
1743                        }
1744                        indexing::ConstBlock::Node(node) => {
1745                            node.parse(|p| crate::hir::lowering2::block(&mut hir_ctx, None, p))?
1746                        }
1747                    };
1748
1749                    let mut cx = ir::Ctxt {
1750                        source_id: item_meta.location.source_id,
1751                        q: self.borrow(),
1752                    };
1753                    ir::Ir::new(item_meta.location.span, ir::compiler::block(&hir, &mut cx)?)
1754                };
1755
1756                let mut const_compiler = ir::Interpreter {
1757                    budget: ir::Budget::new(1_000_000),
1758                    scopes: ir::Scopes::new()?,
1759                    module: item_meta.module,
1760                    item: item_meta.item,
1761                    q: self.borrow(),
1762                };
1763
1764                let const_value = const_compiler.eval_const(&ir, used)?;
1765
1766                let hash = self.pool.item_type_hash(item_meta.item);
1767                self.inner.constants.try_insert(hash, const_value)?;
1768
1769                if used.is_unused() {
1770                    self.inner.queue.try_push_back(BuildEntry {
1771                        item_meta,
1772                        build: Build::Unused,
1773                    })?;
1774                }
1775
1776                meta::Kind::Const
1777            }
1778            Indexed::ConstFn(c) => {
1779                let (ir_fn, hir) = {
1780                    let mut cx = crate::hir::Ctxt::with_const(
1781                        self.const_arena,
1782                        self.borrow(),
1783                        item_meta.location.source_id,
1784                    )?;
1785
1786                    let hir = match &c {
1787                        indexing::ConstFn::Ast(ast) => crate::hir::lowering::item_fn(&mut cx, ast)?,
1788                        indexing::ConstFn::Node(node) => {
1789                            node.parse(|p| crate::hir::lowering2::item_fn(&mut cx, p, false))?
1790                        }
1791                    };
1792
1793                    let mut cx = ir::Ctxt {
1794                        source_id: item_meta.location.source_id,
1795                        q: self.borrow(),
1796                    };
1797                    (ir::IrFn::compile_ast(&hir, &mut cx)?, hir)
1798                };
1799
1800                self.inner.const_fns.try_insert(
1801                    item_meta.item,
1802                    Rc::new(ConstFn {
1803                        item_meta,
1804                        ir_fn,
1805                        hir,
1806                    }),
1807                )?;
1808
1809                if used.is_unused() {
1810                    self.inner.queue.try_push_back(BuildEntry {
1811                        item_meta,
1812                        build: Build::Unused,
1813                    })?;
1814                }
1815
1816                meta::Kind::ConstFn
1817            }
1818            Indexed::Import(import) => {
1819                if !import.wildcard {
1820                    self.inner.queue.try_push_back(BuildEntry {
1821                        item_meta,
1822                        build: Build::Import(import),
1823                    })?;
1824                }
1825
1826                meta::Kind::Import(import.entry)
1827            }
1828            Indexed::Module => meta::Kind::Module,
1829        };
1830
1831        let source = SourceMeta {
1832            location: item_meta.location,
1833            path: self
1834                .sources
1835                .path(item_meta.location.source_id)
1836                .map(|p| p.try_into())
1837                .transpose()?,
1838        };
1839
1840        Ok(meta::Meta {
1841            context: false,
1842            hash: self.pool.item_type_hash(item_meta.item),
1843            item_meta,
1844            kind,
1845            source: Some(source),
1846            parameters: Hash::EMPTY,
1847        })
1848    }
1849
1850    /// Insert the given name into the unit.
1851    fn insert_name(&mut self, item: ItemId) -> alloc::Result<()> {
1852        let item = self.pool.item(item);
1853        self.inner.names.insert(item)?;
1854        Ok(())
1855    }
1856
1857    /// Handle an imported indexed entry.
1858    fn import_indexed(
1859        &mut self,
1860        span: &dyn Spanned,
1861        item_meta: ItemMeta,
1862        indexed: Indexed,
1863        used: Used,
1864    ) -> compile::Result<()> {
1865        // NB: if we find another indexed entry, queue it up for
1866        // building and clone its built meta to the other
1867        // results.
1868        let entry = indexing::Entry { item_meta, indexed };
1869
1870        let meta = self.build_indexed_entry(span, entry, used)?;
1871        self.unit.insert_meta(span, &meta, self.pool, self.inner)?;
1872        self.insert_meta(meta).with_span(span)?;
1873        Ok(())
1874    }
1875
1876    /// Remove the indexed entry corresponding to the given item..
1877    fn remove_indexed(
1878        &mut self,
1879        span: &dyn Spanned,
1880        item: ItemId,
1881    ) -> compile::Result<Option<indexing::Entry>> {
1882        // See if there's an index entry we can construct and insert.
1883        let Some(entries) = self.inner.indexed.remove(&item) else {
1884            return Ok(None);
1885        };
1886
1887        let mut it = entries.into_iter().peekable();
1888
1889        let Some(mut cur) = it.next() else {
1890            return Ok(None);
1891        };
1892
1893        if it.peek().is_none() {
1894            return Ok(Some(cur));
1895        }
1896
1897        let mut locations = try_vec![(cur.item_meta.location, cur.item())];
1898
1899        while let Some(oth) = it.next() {
1900            locations.try_push((oth.item_meta.location, oth.item()))?;
1901
1902            if let (Indexed::Import(a), Indexed::Import(b)) = (&cur.indexed, &oth.indexed) {
1903                if a.wildcard {
1904                    cur = oth;
1905                    continue;
1906                }
1907
1908                if b.wildcard {
1909                    continue;
1910                }
1911            }
1912
1913            for oth in it {
1914                locations.try_push((oth.item_meta.location, oth.item()))?;
1915            }
1916
1917            return Err(compile::Error::new(
1918                span,
1919                ErrorKind::AmbiguousItem {
1920                    item: self.pool.item(cur.item_meta.item).try_to_owned()?,
1921                    #[cfg(feature = "emit")]
1922                    locations: locations
1923                        .into_iter()
1924                        .map(|(loc, item)| Ok((loc, self.pool.item(item).try_to_owned()?)))
1925                        .try_collect::<alloc::Result<_>>()??,
1926                },
1927            ));
1928        }
1929
1930        if let Indexed::Import(indexing::Import { wildcard: true, .. }) = &cur.indexed {
1931            return Err(compile::Error::new(
1932                span,
1933                ErrorKind::AmbiguousItem {
1934                    item: self.pool.item(cur.item_meta.item).try_to_owned()?,
1935                    #[cfg(feature = "emit")]
1936                    locations: locations
1937                        .into_iter()
1938                        .map(|(loc, item)| Ok((loc, self.pool.item(item).try_to_owned()?)))
1939                        .try_collect::<alloc::Result<_>>()??,
1940                },
1941            ));
1942        }
1943
1944        Ok(Some(cur))
1945    }
1946
1947    /// Walk the names to find the first one that is contained in the unit.
1948    #[tracing::instrument(skip_all, fields(module = ?self.pool.module_item(module), base = ?self.pool.item(item)))]
1949    fn convert_initial_path(
1950        &mut self,
1951        module: ModId,
1952        item: ItemId,
1953        local: &ast::Ident,
1954        used: Used,
1955    ) -> compile::Result<ItemId> {
1956        let mut base = self.pool.item(item).try_to_owned()?;
1957        debug_assert!(base.starts_with(self.pool.module_item(module)));
1958
1959        let local_str = local.resolve(resolve_context!(self))?.try_to_owned()?;
1960
1961        while base.starts_with(self.pool.module_item(module)) {
1962            base.push(&local_str)?;
1963            tracing::trace!(?base, "testing");
1964
1965            if self.inner.names.contains(&base)? {
1966                let item = self.pool.alloc_item(&base)?;
1967
1968                // TODO: We probably should not engage the whole query meta
1969                // machinery here.
1970                if let Some(meta) = self.query_meta(local, item, used)? {
1971                    tracing::trace!(?base, ?meta.kind, "testing found meta");
1972
1973                    if !matches!(
1974                        meta.kind,
1975                        meta::Kind::Function {
1976                            associated: Some(..),
1977                            ..
1978                        }
1979                    ) {
1980                        return Ok(self.pool.alloc_item(base)?);
1981                    }
1982                }
1983            }
1984
1985            let c = base.pop();
1986            debug_assert!(c);
1987
1988            if !base.pop() {
1989                break;
1990            }
1991        }
1992
1993        if let Some(item) = self.prelude.get(&local_str) {
1994            return Ok(self.pool.alloc_item(item)?);
1995        }
1996
1997        if self.context.contains_crate(&local_str) {
1998            return Ok(self.pool.alloc_item(ItemBuf::with_crate(&local_str)?)?);
1999        }
2000
2001        let new_module = self.pool.module_item(module).extended(&local_str)?;
2002        Ok(self.pool.alloc_item(new_module)?)
2003    }
2004
2005    /// Check that the given item is accessible from the given module.
2006    fn check_access_to(
2007        &mut self,
2008        span: &dyn Spanned,
2009        from: ModId,
2010        item: ItemId,
2011        module: ModId,
2012        #[cfg(feature = "emit")] location: Location,
2013        visibility: Visibility,
2014        #[cfg(feature = "emit")] chain: &mut Vec<ImportStep>,
2015    ) -> compile::Result<()> {
2016        #[cfg(feature = "emit")]
2017        fn into_chain(chain: Vec<ImportStep>) -> alloc::Result<Vec<Location>> {
2018            chain.into_iter().map(|c| c.location).try_collect()
2019        }
2020
2021        let (common, tree) = self
2022            .pool
2023            .module_item(from)
2024            .ancestry(self.pool.module_item(module))?;
2025
2026        let mut current_module = common.try_clone()?;
2027
2028        // Check each module from the common ancestrly to the module.
2029        for c in &tree {
2030            current_module.push(c)?;
2031            let current_module_id = self.pool.alloc_item(&current_module)?;
2032
2033            let Some(m) = self.pool.module_by_item(current_module_id) else {
2034                return Err(compile::Error::new(
2035                    span,
2036                    ErrorKind::MissingMod {
2037                        item: current_module.try_clone()?,
2038                    },
2039                ));
2040            };
2041
2042            if !m.visibility.is_visible(&common, &current_module) {
2043                return Err(compile::Error::new(
2044                    span,
2045                    ErrorKind::NotVisibleMod {
2046                        #[cfg(feature = "emit")]
2047                        chain: into_chain(take(chain))?,
2048                        #[cfg(feature = "emit")]
2049                        location: m.location,
2050                        visibility: m.visibility,
2051                        item: current_module,
2052                        from: self.pool.module_item(from).try_to_owned()?,
2053                    },
2054                ));
2055            }
2056        }
2057
2058        if !visibility.is_visible_inside(&common, self.pool.module_item(module)) {
2059            return Err(compile::Error::new(
2060                span,
2061                ErrorKind::NotVisible {
2062                    #[cfg(feature = "emit")]
2063                    chain: into_chain(take(chain))?,
2064                    #[cfg(feature = "emit")]
2065                    location,
2066                    visibility,
2067                    item: self.pool.item(item).try_to_owned()?,
2068                    from: self.pool.module_item(from).try_to_owned()?,
2069                },
2070            ));
2071        }
2072
2073        Ok(())
2074    }
2075
2076    /// Get a constant value.
2077    pub(crate) fn get_const_value(&self, hash: Hash) -> Option<&ConstValue> {
2078        if let Some(const_value) = self.inner.constants.get(&hash) {
2079            return Some(const_value);
2080        }
2081
2082        self.context.get_const_value(hash)
2083    }
2084}
2085
2086struct FoundImportStep {
2087    item_meta: ItemMeta,
2088    import: meta::Import,
2089}