rune/compile/
context.rs

1use core::fmt;
2
3use crate as rune;
4use crate::alloc::prelude::*;
5use crate::alloc::{self, BTreeSet, Box, HashMap, HashSet, String, Vec};
6#[cfg(feature = "emit")]
7use crate::compile::MetaInfo;
8use crate::compile::{self, ContextError, Names};
9use crate::compile::{meta, Docs};
10use crate::function::{Function, Plain};
11use crate::function_meta::{AssociatedName, ToInstance};
12use crate::hash;
13use crate::item::{ComponentRef, IntoComponent};
14use crate::macros::{MacroContext, TokenStream};
15use crate::module::{
16    DocFunction, Fields, Module, ModuleAssociated, ModuleAssociatedKind, ModuleFunction,
17    ModuleItem, ModuleItemCommon, ModuleReexport, ModuleTrait, ModuleTraitImpl, ModuleType,
18    TypeSpecification,
19};
20use crate::runtime::{
21    Address, AnyTypeInfo, ConstConstructImpl, ConstContext, ConstValue, FunctionHandler, Memory,
22    Output, Protocol, Rtti, RttiKind, RuntimeContext, TypeInfo, VmError,
23};
24use crate::sync::Arc;
25use crate::{Hash, Item, ItemBuf};
26
27crate::declare_dyn_fn! {
28    struct MacroHandlerVtable;
29
30    /// A (type erased) macro handler.
31    pub struct MacroHandler {
32        fn call(cx: &mut MacroContext<'_, '_, '_>, input: &TokenStream) -> compile::Result<TokenStream>;
33    }
34}
35
36crate::declare_dyn_fn! {
37    struct TraitHandlerVtable;
38
39    /// Invoked when types implement a trait.
40    pub struct TraitHandler {
41        fn call(cx: &mut TraitContext<'_>) -> Result<(), ContextError>;
42    }
43}
44
45crate::declare_dyn_fn! {
46    struct AttributeMacroHandlerVtable;
47
48    /// A (type erased) attribute macro handler.
49    pub struct AttributeMacroHandler {
50        fn call(cx: &mut MacroContext<'_, '_, '_>, input: &TokenStream, attributes: &TokenStream) -> compile::Result<TokenStream>;
51    }
52}
53
54/// Type used to install traits.
55pub struct TraitContext<'a> {
56    /// The context the trait function are being installed into.
57    cx: &'a mut Context,
58    /// The item being installed.
59    item: &'a Item,
60    /// The hash of the item being installed.
61    hash: Hash,
62    /// Type info of the type being installed.
63    type_info: &'a TypeInfo,
64    /// The trait being implemented for.
65    trait_item: &'a Item,
66    /// Hash of the trait being impleemnted.
67    trait_hash: Hash,
68}
69
70impl TraitContext<'_> {
71    /// Return the item the trait is being installed for.
72    pub fn item(&self) -> &Item {
73        self.item
74    }
75
76    /// Return the hash the trait is being installed for.
77    pub fn hash(&self) -> Hash {
78        self.hash
79    }
80
81    /// Find the given protocol function for the current type.
82    ///
83    /// This requires that the function is defined.
84    pub fn find(&mut self, protocol: &'static Protocol) -> Result<FunctionHandler, ContextError> {
85        let name = protocol.to_instance()?;
86
87        let hash = name
88            .kind
89            .hash(self.hash)
90            .with_function_parameters(name.function_parameters);
91
92        let Some(handler) = self.cx.functions.get(&hash) else {
93            return Err(ContextError::MissingTraitFunction {
94                name: name.kind.try_to_string()?,
95                item: self.item.try_to_owned()?,
96                hash,
97                trait_item: self.trait_item.try_to_owned()?,
98                trait_hash: self.trait_hash,
99            });
100        };
101
102        let handler = handler.clone();
103
104        if let Some(method) = protocol.method {
105            self.function_handler(method, &handler)?;
106        }
107
108        Ok(handler)
109    }
110
111    /// Try to find the given associated function.
112    ///
113    /// This does not require that the function is defined.
114    pub fn try_find(&self, name: impl ToInstance) -> Result<Option<FunctionHandler>, ContextError> {
115        let name = name.to_instance()?;
116
117        let hash = name
118            .kind
119            .hash(self.hash)
120            .with_function_parameters(name.function_parameters);
121
122        Ok(self.cx.functions.get(&hash).cloned())
123    }
124
125    /// Find or define a protocol function.
126    pub fn find_or_define<A, F>(
127        &mut self,
128        protocol: &'static Protocol,
129        function: F,
130    ) -> Result<FunctionHandler, ContextError>
131    where
132        F: Function<A, Plain>,
133    {
134        let function = if let Some(function) = self.try_find(protocol)? {
135            function
136        } else {
137            self.function(protocol, function)?
138        };
139
140        if let Some(method) = protocol.method {
141            self.function_handler(method, &function)?;
142        }
143
144        Ok(function)
145    }
146
147    /// Define a new associated function for the current type.
148    pub fn function<F, A>(
149        &mut self,
150        name: impl ToInstance,
151        handler: F,
152    ) -> Result<FunctionHandler, ContextError>
153    where
154        F: Function<A, Plain>,
155    {
156        let handler = FunctionHandler::new(move |memory, addr, len, out| {
157            handler.call(memory, addr, len, out)
158        })?;
159        self.function_handler(name, &handler)?;
160        Ok(handler)
161    }
162
163    /// Define a new associated raw function for the current type.
164    pub fn raw_function<F>(
165        &mut self,
166        name: impl ToInstance,
167        handler: F,
168    ) -> Result<FunctionHandler, ContextError>
169    where
170        F: 'static
171            + Fn(&mut dyn Memory, Address, usize, Output) -> Result<(), VmError>
172            + Send
173            + Sync,
174    {
175        let handler = FunctionHandler::new(handler)?;
176        self.function_handler(name, &handler)?;
177        Ok(handler)
178    }
179
180    /// Define a new associated function for the current type using a raw
181    /// handler.
182    fn function_handler(
183        &mut self,
184        name: impl ToInstance,
185        handler: &FunctionHandler,
186    ) -> Result<(), ContextError> {
187        let name = name.to_instance()?;
188        self.function_inner(name, handler)
189    }
190
191    fn function_inner(
192        &mut self,
193        name: AssociatedName,
194        handler: &FunctionHandler,
195    ) -> Result<(), ContextError> {
196        let function = ModuleFunction {
197            handler: handler.clone(),
198            trait_hash: Some(self.trait_hash),
199            doc: DocFunction {
200                #[cfg(feature = "doc")]
201                is_async: false,
202                #[cfg(feature = "doc")]
203                args: None,
204                #[cfg(feature = "doc")]
205                argument_types: Box::default(),
206                #[cfg(feature = "doc")]
207                return_type: meta::DocType::empty(),
208            },
209        };
210
211        let assoc = ModuleAssociated {
212            container: self.hash,
213            container_type_info: self.type_info.try_clone()?,
214            name,
215            common: ModuleItemCommon {
216                docs: Docs::EMPTY,
217                deprecated: None,
218            },
219            kind: ModuleAssociatedKind::Function(function),
220        };
221
222        self.cx.install_associated(&assoc)?;
223        Ok(())
224    }
225}
226
227/// Context metadata.
228#[derive(Debug)]
229#[non_exhaustive]
230pub(crate) struct ContextMeta {
231    /// Type hash for the given meta item.
232    pub(crate) hash: Hash,
233    /// The item of the returned compile meta.
234    pub(crate) item: Option<ItemBuf>,
235    /// The kind of the compile meta.
236    pub(crate) kind: meta::Kind,
237    /// Deprecation notice.
238    #[cfg(feature = "doc")]
239    pub(crate) deprecated: Option<Box<str>>,
240    /// Documentation associated with a context meta.
241    #[cfg(feature = "doc")]
242    pub(crate) docs: Docs,
243}
244
245impl ContextMeta {
246    #[cfg(feature = "emit")]
247    pub(crate) fn info(&self) -> alloc::Result<MetaInfo> {
248        MetaInfo::new(&self.kind, self.hash, self.item.as_deref())
249    }
250}
251
252/// Information on a specific type.
253#[derive(Debug, TryClone)]
254#[non_exhaustive]
255pub(crate) struct ContextType {
256    /// Item of the type.
257    item: ItemBuf,
258    /// Type hash.
259    hash: Hash,
260    /// Complete detailed information on the hash.
261    type_info: TypeInfo,
262    /// Type parameters.
263    type_parameters: Hash,
264}
265
266impl fmt::Display for ContextType {
267    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
268        write!(fmt, "{} => {}", self.item, self.type_info)?;
269        Ok(())
270    }
271}
272
273/// [Context] used for the Rune language.
274///
275/// See [Build::with_context][crate::Build::with_context].
276///
277/// At runtime this needs to be converted into a [RuntimeContext] when used with
278/// a [Vm][crate::runtime::Vm]. This is done through [Context::runtime].
279///
280/// A [Context] contains:
281/// * Native functions.
282/// * Native instance functions.
283/// * And native type definitions.
284#[derive(Default)]
285pub struct Context {
286    /// Unique modules installed in the context.
287    unique: HashSet<&'static str>,
288    /// Whether or not to include the prelude when constructing a new unit.
289    has_default_modules: bool,
290    /// Registered metadata, in the order that it was registered.
291    meta: Vec<ContextMeta>,
292    /// Item metadata in the context.
293    hash_to_meta: hash::Map<Vec<usize>>,
294    /// Store item to hash mapping.
295    item_to_hash: HashMap<ItemBuf, BTreeSet<Hash>>,
296    /// Registered native function handlers.
297    functions: hash::Map<FunctionHandler>,
298    /// Registered deprecation mesages for native functions.
299    deprecations: hash::Map<String>,
300    /// Information on associated types.
301    #[cfg(feature = "doc")]
302    associated: hash::Map<Vec<Hash>>,
303    /// Traits implemented by the given hash.
304    #[cfg(feature = "doc")]
305    implemented_traits: hash::Map<Vec<Hash>>,
306    /// Registered native macro handlers.
307    macros: hash::Map<MacroHandler>,
308    /// Handlers for realising traits.
309    traits: hash::Map<Option<TraitHandler>>,
310    /// Registered native attribute macro handlers.
311    attribute_macros: hash::Map<AttributeMacroHandler>,
312    /// Registered types.
313    types: hash::Map<ContextType>,
314    /// All available names in the context.
315    names: Names,
316    /// Registered crates.
317    crates: HashSet<Box<str>>,
318    /// Constants visible in this context
319    constants: hash::Map<ConstValue>,
320    /// Constant constructor.
321    construct: hash::Map<ConstConstructImpl>,
322}
323
324impl Context {
325    /// Construct a new empty [Context].
326    #[inline]
327    pub fn new() -> Self {
328        Self::default()
329    }
330
331    /// Construct a [Context] containing the default set of modules with the
332    /// given configuration.
333    ///
334    /// `stdio` determines if we include I/O functions that interact with stdout
335    /// and stderr by default, like `dbg`, `print`, and `println`. If this is
336    /// `false` all the corresponding low-level I/O functions have to be
337    /// provided through a different module.
338    ///
339    /// These are:
340    ///
341    /// * `::std::io::dbg`
342    /// * `::std::io::print`
343    /// * `::std::io::println`
344    pub fn with_config(#[allow(unused)] stdio: bool) -> Result<Self, ContextError> {
345        let mut this = Self::new();
346
347        // NB: Order is important, since later modules might use types defined
348        // in previous modules.
349
350        this.install(crate::modules::iter::module()?)?;
351        this.install(crate::modules::core::module()?)?;
352        this.install(crate::modules::cmp::module()?)?;
353        this.install(crate::modules::any::module()?)?;
354        this.install(crate::modules::clone::module()?)?;
355        this.install(crate::modules::num::module()?)?;
356        this.install(crate::modules::hash::module()?)?;
357
358        this.install(crate::modules::string::module()?)?;
359        this.install(crate::modules::bytes::module()?)?;
360
361        this.install(crate::modules::collections::module()?)?;
362        this.install(crate::modules::collections::hash_map::module()?)?;
363        this.install(crate::modules::collections::hash_set::module()?)?;
364        this.install(crate::modules::collections::vec_deque::module()?)?;
365
366        this.install(crate::modules::char::module()?)?;
367        this.install(crate::modules::f64::module()?)?;
368        this.install(crate::modules::f64::consts::module()?)?;
369        this.install(crate::modules::tuple::module()?)?;
370        this.install(crate::modules::fmt::module()?)?;
371        this.install(crate::modules::future::module()?)?;
372        this.install(crate::modules::i64::module()?)?;
373        this.install(crate::modules::u64::module()?)?;
374        this.install(crate::modules::io::module(stdio)?)?;
375        this.install(crate::modules::macros::module()?)?;
376        this.install(crate::modules::macros::builtin::module()?)?;
377        this.install(crate::modules::mem::module()?)?;
378        this.install(crate::modules::object::module()?)?;
379        this.install(crate::modules::ops::module()?)?;
380        this.install(crate::modules::ops::generator::module()?)?;
381        this.install(crate::modules::option::module()?)?;
382        this.install(crate::modules::result::module()?)?;
383        this.install(crate::modules::stream::module()?)?;
384        this.install(crate::modules::test::module()?)?;
385        this.install(crate::modules::vec::module()?)?;
386        this.install(crate::modules::slice::module()?)?;
387        this.has_default_modules = true;
388        Ok(this)
389    }
390
391    /// Construct a new collection of functions with default packages installed.
392    pub fn with_default_modules() -> Result<Self, ContextError> {
393        Self::with_config(true)
394    }
395
396    /// Construct a runtime context used when executing the virtual machine.
397    ///
398    /// This is not a cheap operation, since it requires cloning things out of
399    /// the build-time [Context] which are necessary at runtime.
400    ///
401    /// ```no_run
402    /// use rune::{Context, Vm, Unit};
403    /// use rune::sync::Arc;
404    ///
405    /// let context = Context::with_default_modules()?;
406    ///
407    /// let runtime = Arc::try_new(context.runtime()?)?;
408    /// let unit = Arc::try_new(Unit::default())?;
409    ///
410    /// let vm = Vm::new(runtime, unit);
411    /// # Ok::<_, rune::support::Error>(())
412    /// ```
413    pub fn runtime(&self) -> alloc::Result<RuntimeContext> {
414        Ok(RuntimeContext::new(
415            self.functions.try_clone()?,
416            self.constants.try_clone()?,
417            self.construct.try_clone()?,
418        ))
419    }
420
421    /// Install the specified module.
422    ///
423    /// This installs everything that has been declared in the given [Module]
424    /// and ensures that they are compatible with the overall context, like
425    /// ensuring that a given type is only declared once.
426    #[tracing::instrument(skip_all, fields(item = ?module.as_ref().item))]
427    pub fn install<M>(&mut self, module: M) -> Result<(), ContextError>
428    where
429        M: AsRef<Module>,
430    {
431        let module = module.as_ref();
432        tracing::trace!("installing");
433
434        if let Some(id) = module.unique {
435            if !self.unique.try_insert(id)? {
436                return Ok(());
437            }
438        }
439
440        if let Some(ComponentRef::Crate(name)) = module.item.first() {
441            self.crates.try_insert(name.try_into()?)?;
442        }
443
444        tracing::trace!("module");
445        self.install_module(module)?;
446
447        tracing::trace!(types = module.types.len(), "types");
448        for ty in &module.types {
449            self.install_type(ty)?;
450        }
451
452        tracing::trace!(traits = module.traits.len(), "traits");
453        for t in &module.traits {
454            self.install_trait(t)?;
455        }
456
457        tracing::trace!(items = module.items.len(), "items");
458        for item in &module.items {
459            self.install_item(item)?;
460        }
461
462        tracing::trace!(associated = module.associated.len(), "associated");
463        for assoc in &module.associated {
464            self.install_associated(assoc)?;
465        }
466
467        tracing::trace!(trait_impls = module.trait_impls.len(), "trait impls");
468        for t in &module.trait_impls {
469            self.install_trait_impl(t)?;
470        }
471
472        tracing::trace!(reexports = module.reexports.len(), "reexports");
473        for r in &module.reexports {
474            self.install_reexport(r)?;
475        }
476
477        tracing::trace!(construct = module.construct.len(), "construct");
478        for (hash, type_info, construct) in &module.construct {
479            self.install_construct(*hash, type_info, construct)?;
480        }
481
482        Ok(())
483    }
484
485    /// Iterate over all available functions in the [Context].
486    #[cfg(any(feature = "cli", feature = "languageserver"))]
487    pub(crate) fn iter_functions(&self) -> impl Iterator<Item = (&ContextMeta, &meta::Signature)> {
488        self.meta.iter().flat_map(|meta| {
489            let signature = meta.kind.as_signature()?;
490            Some((meta, signature))
491        })
492    }
493
494    /// Iterate over all available types in the [Context].
495    #[cfg(feature = "cli")]
496    pub(crate) fn iter_types(&self) -> impl Iterator<Item = (Hash, &Item)> {
497        use core::iter;
498
499        let mut it = self.types.iter();
500
501        iter::from_fn(move || {
502            let (hash, ty) = it.next()?;
503            Some((*hash, ty.item.as_ref()))
504        })
505    }
506
507    /// Iterate over known child components of the given name.
508    pub(crate) fn iter_components<'a, I>(
509        &'a self,
510        iter: I,
511    ) -> alloc::Result<impl Iterator<Item = ComponentRef<'a>> + 'a>
512    where
513        I: 'a + IntoIterator<Item: IntoComponent>,
514    {
515        self.names.iter_components(iter)
516    }
517
518    /// Access the context meta for the given item.
519    ///
520    /// If this returns `Some`, at least one context meta is guaranteed to be
521    /// available.
522    pub(crate) fn lookup_meta(
523        &self,
524        item: &Item,
525    ) -> Option<impl Iterator<Item = &ContextMeta> + Clone> {
526        let hashes = self.item_to_hash.get(item)?;
527
528        Some(hashes.iter().flat_map(|hash| {
529            let indexes = self
530                .hash_to_meta
531                .get(hash)
532                .map(Vec::as_slice)
533                .unwrap_or_default();
534            indexes.iter().map(|&i| &self.meta[i])
535        }))
536    }
537
538    /// Lookup meta by its hash.
539    #[cfg(any(feature = "cli", feature = "languageserver", feature = "emit"))]
540    pub(crate) fn lookup_meta_by_hash(
541        &self,
542        hash: Hash,
543    ) -> impl ExactSizeIterator<Item = &ContextMeta> + Clone {
544        let indexes = self
545            .hash_to_meta
546            .get(&hash)
547            .map(Vec::as_slice)
548            .unwrap_or_default();
549
550        indexes.iter().map(|&i| &self.meta[i])
551    }
552
553    /// Lookup deprecation by function hash.
554    pub fn lookup_deprecation(&self, hash: Hash) -> Option<&str> {
555        self.deprecations.get(&hash).map(|s| s.as_str())
556    }
557
558    /// Check if unit contains the given name by prefix.
559    pub(crate) fn contains_prefix(&self, item: &Item) -> alloc::Result<bool> {
560        self.names.contains_prefix(item)
561    }
562
563    /// Lookup the given native function handler in the context.
564    pub(crate) fn lookup_function(&self, hash: Hash) -> Option<&FunctionHandler> {
565        self.functions.get(&hash)
566    }
567
568    /// Get all associated types for the given hash.
569    #[cfg(all(feature = "doc", feature = "cli"))]
570    pub(crate) fn associated(&self, hash: Hash) -> impl Iterator<Item = Hash> + '_ {
571        self.associated
572            .get(&hash)
573            .map(Vec::as_slice)
574            .unwrap_or_default()
575            .iter()
576            .copied()
577    }
578
579    /// Get all traits implemented for the given hash.
580    #[cfg(all(feature = "doc", feature = "cli"))]
581    pub(crate) fn traits(&self, hash: Hash) -> impl Iterator<Item = Hash> + '_ {
582        self.implemented_traits
583            .get(&hash)
584            .map(Vec::as_slice)
585            .unwrap_or_default()
586            .iter()
587            .copied()
588    }
589
590    /// Lookup the given macro handler.
591    pub(crate) fn lookup_macro(&self, hash: Hash) -> Option<&MacroHandler> {
592        self.macros.get(&hash)
593    }
594
595    /// Lookup the given attribute macro handler.
596    pub(crate) fn lookup_attribute_macro(&self, hash: Hash) -> Option<&AttributeMacroHandler> {
597        self.attribute_macros.get(&hash)
598    }
599
600    /// Iterate over available crates.
601    #[cfg(feature = "cli")]
602    pub(crate) fn iter_crates(&self) -> impl Iterator<Item = &str> {
603        self.crates.iter().map(|s| s.as_ref())
604    }
605
606    /// Check if context contains the given crate.
607    pub(crate) fn contains_crate(&self, name: &str) -> bool {
608        self.crates.contains(name)
609    }
610
611    /// Test if the context has the default modules installed.
612    ///
613    /// This determines among other things whether a prelude should be used or
614    /// not.
615    pub(crate) fn has_default_modules(&self) -> bool {
616        self.has_default_modules
617    }
618
619    /// Try to find an existing module.
620    fn find_existing_module(&self, hash: Hash) -> Option<usize> {
621        let indexes = self.hash_to_meta.get(&hash)?;
622
623        for &index in indexes {
624            let Some(m) = self.meta.get(index) else {
625                continue;
626            };
627
628            if matches!(m.kind, meta::Kind::Module) {
629                return Some(index);
630            }
631        }
632
633        None
634    }
635
636    /// Install the given meta.
637    #[tracing::instrument(skip_all)]
638    fn install_meta(&mut self, meta: ContextMeta) -> Result<(), ContextError> {
639        if let Some(item) = &meta.item {
640            tracing::trace!(?item);
641
642            self.names.insert(item)?;
643
644            self.item_to_hash
645                .entry(item.try_clone()?)
646                .or_try_default()?
647                .try_insert(meta.hash)?;
648        }
649
650        #[cfg(feature = "doc")]
651        if let Some(h) = meta.kind.associated_container() {
652            let assoc = self.associated.entry(h).or_try_default()?;
653            assoc.try_push(meta.hash)?;
654        }
655
656        let hash = meta.hash;
657
658        let index = self.meta.len();
659
660        self.meta.try_push(meta)?;
661
662        self.hash_to_meta
663            .entry(hash)
664            .or_try_default()?
665            .try_push(index)?;
666
667        Ok(())
668    }
669
670    /// Install a module, ensuring that its meta is defined.
671    fn install_module(&mut self, m: &Module) -> Result<(), ContextError> {
672        self.names.insert(&m.item)?;
673
674        let mut current = Some((m.item.as_ref(), Some(&m.common)));
675
676        #[allow(unused)]
677        while let Some((item, common)) = current.take() {
678            let hash = Hash::type_hash(item);
679
680            if let Some(index) = self.find_existing_module(hash) {
681                #[cfg(feature = "doc")]
682                if let Some(common) = common {
683                    let meta = &mut self.meta[index];
684                    meta.deprecated = common.deprecated.try_clone()?;
685                    meta.docs = common.docs.try_clone()?;
686                }
687            } else {
688                self.install_meta(ContextMeta {
689                    hash,
690                    item: Some(item.try_to_owned()?),
691                    kind: meta::Kind::Module,
692                    #[cfg(feature = "doc")]
693                    deprecated: common
694                        .map(|c| c.deprecated.as_ref().try_cloned())
695                        .transpose()?
696                        .flatten(),
697                    #[cfg(feature = "doc")]
698                    docs: common
699                        .map(|c| c.docs.try_clone())
700                        .transpose()?
701                        .unwrap_or_default(),
702                })?;
703            }
704
705            current = item.parent().map(|item| (item, None));
706        }
707
708        Ok(())
709    }
710
711    /// Install a single type.
712    fn install_type(&mut self, ty: &ModuleType) -> Result<(), ContextError> {
713        self.install_type_info(ContextType {
714            item: ty.item.try_to_owned()?,
715            hash: ty.hash,
716            type_info: ty.type_info.try_clone()?,
717            type_parameters: ty.type_parameters,
718        })?;
719
720        let parameters = Hash::EMPTY.with_type_parameters(ty.type_parameters);
721
722        let kind = if let Some(spec) = &ty.spec {
723            match spec {
724                TypeSpecification::Struct(fields) => {
725                    let constructor = match &ty.constructor {
726                        Some(c) => {
727                            let signature = meta::Signature {
728                                #[cfg(feature = "doc")]
729                                is_async: false,
730                                #[cfg(feature = "doc")]
731                                arguments: Some(fields_to_arguments(fields)?),
732                                #[cfg(feature = "doc")]
733                                return_type: meta::DocType::new(ty.hash),
734                            };
735
736                            if c.args != fields.len() {
737                                return Err(ContextError::ConstructorArgumentsMismatch {
738                                    type_info: ty.type_info.try_clone()?,
739                                    expected: fields.len(),
740                                    actual: c.args,
741                                });
742                            }
743
744                            self.insert_native_fn(&ty.type_info, ty.hash, &c.handler, None)?;
745                            Some(signature)
746                        }
747                        None => None,
748                    };
749
750                    meta::Kind::Struct {
751                        fields: match fields {
752                            Fields::Named(fields) => meta::Fields::Named(meta::FieldsNamed {
753                                fields: fields
754                                    .iter()
755                                    .copied()
756                                    .enumerate()
757                                    .map(|(position, name)| {
758                                        Ok(meta::FieldMeta {
759                                            name: name.try_into()?,
760                                            position,
761                                        })
762                                    })
763                                    .try_collect::<alloc::Result<_>>()??,
764                            }),
765                            Fields::Unnamed(args) => meta::Fields::Unnamed(*args),
766                            Fields::Empty => meta::Fields::Empty,
767                        },
768                        constructor,
769                        parameters,
770                        enum_hash: Hash::EMPTY,
771                    }
772                }
773                TypeSpecification::Enum(en) => {
774                    for variant in &en.variants {
775                        let Some(fields) = &variant.fields else {
776                            continue;
777                        };
778
779                        let kind = match fields {
780                            Fields::Empty => RttiKind::Empty,
781                            Fields::Unnamed(..) => RttiKind::Tuple,
782                            Fields::Named(..) => RttiKind::Struct,
783                        };
784
785                        let item = ty.item.extended(variant.name)?;
786                        let hash = Hash::type_hash(&item);
787
788                        self.install_type_info(ContextType {
789                            item: item.try_clone()?,
790                            hash,
791                            type_info: TypeInfo::rtti(Arc::try_new(Rtti {
792                                kind,
793                                hash: ty.hash,
794                                variant_hash: hash,
795                                item: item.try_clone()?,
796                                fields: fields.to_fields()?,
797                            })?),
798                            type_parameters: Hash::EMPTY,
799                        })?;
800
801                        let constructor = if let Some(c) = &variant.constructor {
802                            let signature = meta::Signature {
803                                #[cfg(feature = "doc")]
804                                is_async: false,
805                                #[cfg(feature = "doc")]
806                                arguments: Some(fields_to_arguments(fields)?),
807                                #[cfg(feature = "doc")]
808                                return_type: meta::DocType::new(ty.hash),
809                            };
810
811                            if c.args != fields.len() {
812                                return Err(ContextError::VariantConstructorArgumentsMismatch {
813                                    type_info: ty.type_info.try_clone()?,
814                                    name: variant.name,
815                                    expected: fields.len(),
816                                    actual: c.args,
817                                });
818                            }
819
820                            self.insert_native_fn(
821                                &item,
822                                hash,
823                                &c.handler,
824                                variant.deprecated.as_deref(),
825                            )?;
826                            Some(signature)
827                        } else {
828                            None
829                        };
830
831                        self.install_meta(ContextMeta {
832                            hash,
833                            item: Some(item),
834                            kind: meta::Kind::Struct {
835                                fields: match fields {
836                                    Fields::Named(names) => {
837                                        meta::Fields::Named(meta::FieldsNamed {
838                                            fields: names
839                                                .iter()
840                                                .copied()
841                                                .enumerate()
842                                                .map(|(position, name)| {
843                                                    Ok(meta::FieldMeta {
844                                                        name: name.try_into()?,
845                                                        position,
846                                                    })
847                                                })
848                                                .try_collect::<alloc::Result<_>>()??,
849                                        })
850                                    }
851                                    Fields::Unnamed(args) => meta::Fields::Unnamed(*args),
852                                    Fields::Empty => meta::Fields::Empty,
853                                },
854                                constructor,
855                                parameters: Hash::EMPTY,
856                                enum_hash: ty.hash,
857                            },
858                            #[cfg(feature = "doc")]
859                            deprecated: variant.deprecated.try_clone()?,
860                            #[cfg(feature = "doc")]
861                            docs: variant.docs.try_clone()?,
862                        })?;
863                    }
864
865                    meta::Kind::Enum { parameters }
866                }
867            }
868        } else {
869            meta::Kind::Type { parameters }
870        };
871
872        self.install_meta(ContextMeta {
873            hash: ty.hash,
874            item: Some(ty.item.try_to_owned()?),
875            kind,
876            #[cfg(feature = "doc")]
877            deprecated: ty.common.deprecated.try_clone()?,
878            #[cfg(feature = "doc")]
879            docs: ty.common.docs.try_clone()?,
880        })?;
881
882        Ok(())
883    }
884
885    fn install_trait(&mut self, t: &ModuleTrait) -> Result<(), ContextError> {
886        if self.traits.try_insert(t.hash, t.handler.clone())?.is_some() {
887            return Err(ContextError::ConflictingTrait {
888                item: t.item.try_clone()?,
889                hash: t.hash,
890            });
891        }
892
893        self.install_meta(ContextMeta {
894            hash: t.hash,
895            item: Some(t.item.try_clone()?),
896            kind: meta::Kind::Trait,
897            #[cfg(feature = "doc")]
898            deprecated: t.common.deprecated.try_clone()?,
899            #[cfg(feature = "doc")]
900            docs: t.common.docs.try_clone()?,
901        })?;
902
903        for f in &t.functions {
904            let signature = meta::Signature::from_context(&f.doc, &f.common)?;
905
906            let kind = meta::Kind::Function {
907                associated: Some(f.name.kind.try_clone()?),
908                trait_hash: None,
909                signature,
910                is_test: false,
911                is_bench: false,
912                parameters: Hash::EMPTY.with_function_parameters(f.name.function_parameters),
913                #[cfg(feature = "doc")]
914                container: Some(t.hash),
915                #[cfg(feature = "doc")]
916                parameter_types: f.name.parameter_types.try_clone()?,
917            };
918
919            let hash = f
920                .name
921                .kind
922                .hash(t.hash)
923                .with_function_parameters(f.name.function_parameters);
924
925            let item = if let meta::AssociatedKind::Instance(name) = &f.name.kind {
926                let item = t.item.extended(name.as_ref())?;
927                let hash = Hash::type_hash(&item);
928                Some((hash, item))
929            } else {
930                None
931            };
932
933            self.install_meta(ContextMeta {
934                hash,
935                item: item.map(|(_, item)| item),
936                kind,
937                #[cfg(feature = "doc")]
938                deprecated: f.common.deprecated.try_clone()?,
939                #[cfg(feature = "doc")]
940                docs: f.common.docs.try_clone()?,
941            })?;
942        }
943
944        Ok(())
945    }
946
947    fn install_trait_impl(&mut self, i: &ModuleTraitImpl) -> Result<(), ContextError> {
948        if !self.types.contains_key(&i.hash) {
949            return Err(ContextError::MissingType {
950                item: i.item.try_to_owned()?,
951                type_info: i.type_info.try_clone()?,
952            });
953        };
954
955        let Some(handler) = self.traits.get(&i.trait_hash).cloned() else {
956            return Err(ContextError::MissingTrait {
957                item: i.trait_item.try_clone()?,
958                hash: i.hash,
959                impl_item: i.item.try_to_owned()?,
960                impl_hash: i.hash,
961            });
962        };
963
964        if let Some(handler) = handler {
965            handler.call(&mut TraitContext {
966                cx: self,
967                item: &i.item,
968                hash: i.hash,
969                type_info: &i.type_info,
970                trait_item: &i.trait_item,
971                trait_hash: i.trait_hash,
972            })?;
973        }
974
975        #[cfg(feature = "doc")]
976        self.implemented_traits
977            .entry(i.hash)
978            .or_try_default()?
979            .try_push(i.trait_hash)?;
980
981        Ok(())
982    }
983
984    fn install_reexport(&mut self, r: &ModuleReexport) -> Result<(), ContextError> {
985        self.install_meta(ContextMeta {
986            hash: r.hash,
987            item: Some(r.item.try_clone()?),
988            kind: meta::Kind::Alias(meta::Alias {
989                to: r.to.try_clone()?,
990            }),
991            #[cfg(feature = "doc")]
992            deprecated: None,
993            #[cfg(feature = "doc")]
994            docs: Docs::EMPTY,
995        })?;
996
997        Ok(())
998    }
999
1000    /// Install a constant constructor.
1001    fn install_construct(
1002        &mut self,
1003        hash: Hash,
1004        type_info: &AnyTypeInfo,
1005        construct: &ConstConstructImpl,
1006    ) -> Result<(), ContextError> {
1007        let old = self.construct.try_insert(hash, construct.clone())?;
1008
1009        if old.is_some() {
1010            return Err(ContextError::ConflictingConstConstruct {
1011                type_info: TypeInfo::from(*type_info),
1012                hash,
1013            });
1014        }
1015
1016        Ok(())
1017    }
1018
1019    fn install_type_info(&mut self, ty: ContextType) -> Result<(), ContextError> {
1020        let item_hash = Hash::type_hash(&ty.item).with_type_parameters(ty.type_parameters);
1021
1022        if ty.hash != item_hash {
1023            return Err(ContextError::TypeHashMismatch {
1024                type_info: ty.type_info,
1025                item: ty.item,
1026                hash: ty.hash,
1027                item_hash,
1028            });
1029        }
1030
1031        self.constants.try_insert(
1032            Hash::associated_function(ty.hash, &Protocol::INTO_TYPE_NAME),
1033            ConstValue::try_from(ty.item.try_to_string()?)?,
1034        )?;
1035
1036        if let Some(old) = self.types.try_insert(ty.hash, ty)? {
1037            return Err(ContextError::ConflictingType {
1038                item: old.item,
1039                type_info: old.type_info,
1040                hash: old.hash,
1041            });
1042        }
1043
1044        Ok(())
1045    }
1046
1047    /// Install a function and check for duplicates.
1048    fn install_item(&mut self, m: &ModuleItem) -> Result<(), ContextError> {
1049        self.names.insert(&m.item)?;
1050
1051        let kind = match &m.kind {
1052            rune::module::ModuleItemKind::Constant(value) => {
1053                self.constants.try_insert(m.hash, value.try_clone()?)?;
1054                meta::Kind::Const
1055            }
1056            rune::module::ModuleItemKind::Function(f) => {
1057                self.constants.try_insert(
1058                    Hash::associated_function(m.hash, &Protocol::INTO_TYPE_NAME),
1059                    ConstValue::try_from(m.item.try_to_string()?)?,
1060                )?;
1061
1062                let signature = meta::Signature::from_context(&f.doc, &m.common)?;
1063
1064                self.insert_native_fn(&m.item, m.hash, &f.handler, m.common.deprecated.as_deref())?;
1065
1066                meta::Kind::Function {
1067                    associated: None,
1068                    trait_hash: f.trait_hash,
1069                    signature,
1070                    is_test: false,
1071                    is_bench: false,
1072                    parameters: Hash::EMPTY,
1073                    #[cfg(feature = "doc")]
1074                    container: None,
1075                    #[cfg(feature = "doc")]
1076                    parameter_types: Vec::new(),
1077                }
1078            }
1079            rune::module::ModuleItemKind::Macro(macro_) => {
1080                self.macros.try_insert(m.hash, macro_.handler.clone())?;
1081                meta::Kind::Macro
1082            }
1083            rune::module::ModuleItemKind::AttributeMacro(macro_) => {
1084                self.attribute_macros
1085                    .try_insert(m.hash, macro_.handler.clone())?;
1086                meta::Kind::AttributeMacro
1087            }
1088        };
1089
1090        self.install_meta(ContextMeta {
1091            hash: m.hash,
1092            item: Some(m.item.try_to_owned()?),
1093            kind,
1094            #[cfg(feature = "doc")]
1095            deprecated: m.common.deprecated.try_clone()?,
1096            #[cfg(feature = "doc")]
1097            docs: m.common.docs.try_clone()?,
1098        })?;
1099
1100        Ok(())
1101    }
1102
1103    fn install_associated(&mut self, assoc: &ModuleAssociated) -> Result<(), ContextError> {
1104        let Some(info) = self.types.get(&assoc.container).try_cloned()? else {
1105            return Err(ContextError::MissingContainer {
1106                container: assoc.container_type_info.try_clone()?,
1107            });
1108        };
1109
1110        let hash = assoc
1111            .name
1112            .kind
1113            .hash(assoc.container)
1114            .with_function_parameters(assoc.name.function_parameters);
1115
1116        // If the associated function is a named instance function - register it
1117        // under the name of the item it corresponds to unless it's a field
1118        // function.
1119        //
1120        // The other alternatives are protocol functions (which are not free)
1121        // and plain hashes.
1122        let item = if let meta::AssociatedKind::Instance(name) = &assoc.name.kind {
1123            let item = info.item.extended(name.as_ref())?;
1124
1125            let hash = Hash::type_hash(&item)
1126                .with_type_parameters(info.type_parameters)
1127                .with_function_parameters(assoc.name.function_parameters);
1128
1129            Some((hash, item))
1130        } else {
1131            None
1132        };
1133
1134        let kind = match &assoc.kind {
1135            ModuleAssociatedKind::Constant(value) => {
1136                if let Some((hash, ..)) = item {
1137                    self.constants.try_insert(hash, value.try_clone()?)?;
1138                }
1139
1140                self.constants.try_insert(hash, value.try_clone()?)?;
1141                meta::Kind::Const
1142            }
1143            ModuleAssociatedKind::Function(f) => {
1144                let signature = meta::Signature::from_context(&f.doc, &assoc.common)?;
1145
1146                if let Some((hash, item)) = &item {
1147                    self.constants.try_insert(
1148                        Hash::associated_function(*hash, &Protocol::INTO_TYPE_NAME),
1149                        ConstValue::try_from(item.try_to_string()?)?,
1150                    )?;
1151
1152                    self.insert_native_fn(
1153                        &assoc.container_type_info,
1154                        *hash,
1155                        &f.handler,
1156                        assoc.common.deprecated.as_deref(),
1157                    )?;
1158                }
1159
1160                self.insert_native_fn(
1161                    &assoc.container_type_info,
1162                    hash,
1163                    &f.handler,
1164                    assoc.common.deprecated.as_deref(),
1165                )?;
1166
1167                meta::Kind::Function {
1168                    associated: Some(assoc.name.kind.try_clone()?),
1169                    trait_hash: f.trait_hash,
1170                    signature,
1171                    is_test: false,
1172                    is_bench: false,
1173                    parameters: Hash::EMPTY
1174                        .with_type_parameters(info.type_parameters)
1175                        .with_function_parameters(assoc.name.function_parameters),
1176                    #[cfg(feature = "doc")]
1177                    container: Some(assoc.container),
1178                    #[cfg(feature = "doc")]
1179                    parameter_types: assoc.name.parameter_types.try_clone()?,
1180                }
1181            }
1182        };
1183
1184        self.install_meta(ContextMeta {
1185            hash,
1186            item: item.map(|(_, item)| item),
1187            kind,
1188            #[cfg(feature = "doc")]
1189            deprecated: assoc.common.deprecated.try_clone()?,
1190            #[cfg(feature = "doc")]
1191            docs: assoc.common.docs.try_clone()?,
1192        })?;
1193
1194        Ok(())
1195    }
1196
1197    fn insert_native_fn(
1198        &mut self,
1199        display: &dyn fmt::Display,
1200        hash: Hash,
1201        handler: &FunctionHandler,
1202        deprecation: Option<&str>,
1203    ) -> Result<(), ContextError> {
1204        if self.functions.contains_key(&hash) {
1205            return Err(ContextError::ConflictingFunction {
1206                part: display.try_to_string()?.try_into()?,
1207                hash,
1208            });
1209        }
1210
1211        self.functions.try_insert(hash, handler.clone())?;
1212
1213        if let Some(msg) = deprecation {
1214            self.deprecations.try_insert(hash, msg.try_to_owned()?)?;
1215        }
1216
1217        Ok(())
1218    }
1219
1220    /// Get a constant value.
1221    pub(crate) fn get_const_value(&self, hash: Hash) -> Option<&ConstValue> {
1222        self.constants.get(&hash)
1223    }
1224}
1225
1226impl fmt::Debug for Context {
1227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1228        write!(f, "Context")
1229    }
1230}
1231
1232impl ConstContext for Context {
1233    #[inline]
1234    fn get(&self, hash: Hash) -> Option<&ConstConstructImpl> {
1235        self.construct.get(&hash)
1236    }
1237}
1238
1239#[cfg(feature = "doc")]
1240fn fields_to_arguments(fields: &Fields) -> alloc::Result<Box<[meta::DocArgument]>> {
1241    match *fields {
1242        Fields::Named(fields) => {
1243            let mut out = Vec::try_with_capacity(fields.len())?;
1244
1245            for &name in fields {
1246                out.try_push(meta::DocArgument {
1247                    name: meta::DocName::Name(Box::try_from(name)?),
1248                    base: Hash::EMPTY,
1249                    generics: Box::default(),
1250                })?;
1251            }
1252
1253            Box::try_from(out)
1254        }
1255        Fields::Unnamed(args) => {
1256            let mut out = Vec::try_with_capacity(args)?;
1257
1258            for n in 0..args {
1259                out.try_push(meta::DocArgument {
1260                    name: meta::DocName::Index(n),
1261                    base: Hash::EMPTY,
1262                    generics: Box::default(),
1263                })?;
1264            }
1265
1266            Box::try_from(out)
1267        }
1268        Fields::Empty => Ok(Box::default()),
1269    }
1270}
1271
1272#[cfg(test)]
1273static_assertions::assert_impl_all!(Context: Send, Sync);