ad_astra/runtime/
ty.rs

1////////////////////////////////////////////////////////////////////////////////
2// This file is part of "Ad Astra", an embeddable scripting programming       //
3// language platform.                                                         //
4//                                                                            //
5// This work is proprietary software with source-available code.              //
6//                                                                            //
7// To copy, use, distribute, or contribute to this work, you must agree to    //
8// the terms of the General License Agreement:                                //
9//                                                                            //
10// https://github.com/Eliah-Lakhin/ad-astra/blob/master/EULA.md               //
11//                                                                            //
12// The agreement grants a Basic Commercial License, allowing you to use       //
13// this work in non-commercial and limited commercial products with a total   //
14// gross revenue cap. To remove this commercial limit for one of your         //
15// products, you must acquire a Full Commercial License.                      //
16//                                                                            //
17// If you contribute to the source code, documentation, or related materials, //
18// you must grant me an exclusive license to these contributions.             //
19// Contributions are governed by the "Contributions" section of the General   //
20// License Agreement.                                                         //
21//                                                                            //
22// Copying the work in parts is strictly forbidden, except as permitted       //
23// under the General License Agreement.                                       //
24//                                                                            //
25// If you do not or cannot agree to the terms of this Agreement,              //
26// do not use this work.                                                      //
27//                                                                            //
28// This work is provided "as is", without any warranties, express or implied, //
29// except where such disclaimers are legally invalid.                         //
30//                                                                            //
31// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин).                 //
32// All rights reserved.                                                       //
33////////////////////////////////////////////////////////////////////////////////
34
35use std::{
36    any::{type_name, TypeId},
37    cmp::Ordering,
38    collections::hash_set::Iter,
39    fmt::{Debug, Display, Formatter},
40    hash::{Hash, Hasher},
41    iter::FusedIterator,
42    mem::transmute,
43    ops::Deref,
44    ptr::NonNull,
45};
46
47use ahash::{AHashMap, AHashSet};
48use lady_deirdre::sync::Lazy;
49
50use crate::{
51    report::debug_unreachable,
52    runtime::{
53        ops::{
54            DynamicType,
55            Fn0Repr,
56            Fn1Repr,
57            Fn2Repr,
58            Fn3Repr,
59            Fn4Repr,
60            Fn5Repr,
61            Fn6Repr,
62            Fn7Repr,
63        },
64        RustOrigin,
65        __intrinsics::DeclarationGroup,
66    },
67};
68
69/// A Rust type that has been registered with the Script Engine.
70///
71/// Whenever you export a Rust type using the [export](crate::export) macro, the
72/// macro automatically registers the [type introspection metadata](TypeMeta)
73/// for that type and implements the ScriptType trait for it.
74///
75/// You cannot (and should not) implement this trait manually.
76pub trait ScriptType: sealed::Sealed + Send + Sync + 'static {
77    /// Returns the introspection metadata of this Rust type registered in the
78    /// Script Engine.
79    #[inline(always)]
80    fn type_meta() -> &'static TypeMeta {
81        match TypeMeta::by_id(&TypeId::of::<Self>()) {
82            Some(meta) => meta,
83
84            None => {
85                let name = type_name::<Self>();
86                panic!("{name} type was not registered. Probably because export has been disabled for this type.")
87            }
88        }
89    }
90}
91
92mod sealed {
93    use crate::runtime::{ScriptType, __intrinsics::RegisteredType};
94
95    pub trait Sealed {}
96
97    impl<T: RegisteredType + ?Sized> Sealed for T {}
98
99    impl<T: RegisteredType + ?Sized> ScriptType for T {}
100}
101
102/// An introspection metadata for the Rust type [registered](ScriptType) by
103/// the Script Engine.
104///
105/// You cannot create this object manually; its creation is managed by the
106/// Script Engine. However, you can obtain a `'static` reference to this object
107/// using the [ScriptType::type_meta] function and other related API functions.
108///
109/// The [Display] implementation prints the user-facing name of the type (e.g.,
110/// `"usize"`, `"str"`, or `"Vec<bool>"`).
111///
112/// This object allows you to explore the type's introspection metadata and the
113/// Script operations available for this type using the [TypeMeta::prototype]
114/// function.
115#[derive(Clone, Copy, Debug)]
116pub struct TypeMeta {
117    id: TypeId,
118    name: &'static str,
119    origin: &'static RustOrigin,
120    doc: Option<&'static str>,
121    family: TypeFamilyInner,
122    size: usize,
123}
124
125impl Default for &'static TypeMeta {
126    #[inline(always)]
127    fn default() -> Self {
128        TypeMeta::nil()
129    }
130}
131
132impl PartialEq for TypeMeta {
133    #[inline(always)]
134    fn eq(&self, other: &Self) -> bool {
135        self.id.eq(&other.id)
136    }
137}
138
139impl PartialEq<TypeId> for TypeMeta {
140    #[inline(always)]
141    fn eq(&self, other: &TypeId) -> bool {
142        self.id.eq(other)
143    }
144}
145
146impl Eq for TypeMeta {}
147
148impl Ord for TypeMeta {
149    #[inline(always)]
150    fn cmp(&self, other: &Self) -> Ordering {
151        self.id.cmp(&other.id)
152    }
153}
154
155impl PartialOrd for TypeMeta {
156    #[inline(always)]
157    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
158        Some(self.cmp(other))
159    }
160}
161
162impl Hash for TypeMeta {
163    #[inline(always)]
164    fn hash<H: Hasher>(&self, state: &mut H) {
165        self.id.hash(state)
166    }
167}
168
169impl Display for TypeMeta {
170    #[inline(always)]
171    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
172        formatter.write_str(self.name)
173    }
174}
175
176impl TypeMeta {
177    /// Returns the metadata of the [unit] `()` type. In Ad Astra terminology,
178    /// the unit type is referred to as the "nil type" and represents
179    /// inaccessible data.
180    ///
181    /// The [Nil Cell](crate::runtime::Cell::nil) corresponds to the Nil type.
182    ///
183    /// This function is a shortcut for `<()>::type_meta()`.
184    #[inline(always)]
185    pub fn nil() -> &'static Self {
186        <()>::type_meta()
187    }
188
189    /// Returns the metadata of the [type placeholder](DynamicType) that cannot
190    /// be analyzed at script compile-time.
191    ///
192    /// This function is a shortcut for `<DynamicType>::type_meta()`.
193    #[inline(always)]
194    pub fn dynamic() -> &'static Self {
195        <DynamicType>::type_meta()
196    }
197
198    #[inline(always)]
199    pub(crate) fn script_fn(arity: usize) -> Option<&'static Self> {
200        match arity {
201            0 => Some(Fn0Repr::type_meta()),
202            1 => Some(Fn1Repr::type_meta()),
203            2 => Some(Fn2Repr::type_meta()),
204            3 => Some(Fn3Repr::type_meta()),
205            4 => Some(Fn4Repr::type_meta()),
206            5 => Some(Fn5Repr::type_meta()),
207            6 => Some(Fn6Repr::type_meta()),
208            7 => Some(Fn7Repr::type_meta()),
209            _ => None,
210        }
211    }
212
213    #[inline(always)]
214    pub(super) fn enumerate() -> impl Iterator<Item = &'static TypeId> {
215        let registry = TypeRegistry::get();
216
217        registry.type_index.keys()
218    }
219
220    #[inline(always)]
221    pub(super) fn by_id(id: &TypeId) -> Option<&'static Self> {
222        let registry = TypeRegistry::get();
223
224        registry.type_index.get(id)
225    }
226
227    /// Returns the [TypeId] of the original Rust type.
228    #[inline(always)]
229    pub fn id(&self) -> &TypeId {
230        &self.id
231    }
232
233    /// Returns true if this type is a [Nil type](Self::nil), which
234    /// represents a void, inaccessible object.
235    #[inline(always)]
236    pub fn is_nil(&self) -> bool {
237        self.id.eq(&TypeId::of::<()>())
238    }
239
240    /// Returns true if the type is a [Dynamic type](DynamicType), a type
241    /// placeholder that cannot be analyzed at script compile-time.
242    #[inline(always)]
243    pub fn is_dynamic(&self) -> bool {
244        self.id.eq(&TypeId::of::<DynamicType>())
245    }
246
247    /// Returns true if this type belongs to the
248    /// [family of functions](TypeFamily::fn_family).
249    ///
250    /// These types typically support the
251    /// [invocation operator](crate::runtime::Object::invoke).
252    ///
253    /// This function is a shortcut for `type_meta_family().is_fn()`.
254    #[inline(always)]
255    pub fn is_fn(&self) -> bool {
256        self.family().is_fn()
257    }
258
259    /// Returns the user-facing name of the original Rust type, such as
260    /// `"usize"`, `"str"`, `"Vec<bool>"`, etc.
261    #[inline(always)]
262    pub fn name(&self) -> &'static str {
263        self.name
264    }
265
266    /// Returns the location in the Rust source code where the Rust type was
267    /// declared (or registered by the [export](crate::export) macro).
268    #[inline(always)]
269    pub fn origin(&self) -> &'static RustOrigin {
270        self.origin
271    }
272
273    /// Returns the RustDoc documentation for the Rust type.
274    ///
275    /// The function returns None if the type does not have documentation or
276    /// if the documentation was not recognized by the [export](crate::export)
277    /// macro.
278    #[inline(always)]
279    pub fn doc(&self) -> Option<&'static str> {
280        self.doc
281    }
282
283    #[inline(always)]
284    pub(super) fn size(&self) -> usize {
285        self.size
286    }
287
288    /// Returns a reference to the family of types to which this Rust type
289    /// belongs.
290    #[inline(always)]
291    pub fn family(&self) -> &TypeFamily {
292        // Safety: Transparent type transmutation.
293        unsafe { transmute::<&TypeFamilyInner, &TypeFamily>(&self.family) }
294    }
295}
296
297/// A set of semantically related types.
298///
299/// Types that can be type-casted to each other to some extent form a
300/// family of types. The semantic analyzer typically treats them as a single
301/// unified type, and the LSP server usually displays the type family to which
302/// a specific type belongs.
303///
304/// This approach simplifies interoperability between Rust types in scripts.
305/// For example, [usize], [f32], and other Rust built-in primitive numeric types
306/// form the `number` family of types. The script engine performs automatic type
307/// conversions between these types, allowing the end user to work with each
308/// specific numeric type as a general "number".
309///
310/// When you export a type using the [export](crate::export) macro, the Script
311/// Engine automatically creates a new type family containing just that type.
312///
313/// However, you can manually associate a type with an existing family using the
314/// `#[export(family(<family_reference>))]` macro option.
315///
316/// To introduce a new type family, consider using the
317/// [type_family](crate::type_family) declarative macro.
318///
319/// The TypeFamily object provides functions to explore the types associated
320/// with the family.
321///
322/// The [IntoIterator] implementation for this object iterates over each
323/// [TypeMeta] associated with this family.
324///
325/// The [Debug] and [Display] implementations print the name of the family, and
326/// (in alternate mode) enumerate the names of all associated types.
327#[repr(transparent)]
328pub struct TypeFamily(TypeFamilyInner);
329
330impl PartialEq for TypeFamily {
331    #[inline]
332    fn eq(&self, other: &Self) -> bool {
333        let this_ptr = match &self.0 {
334            TypeFamilyInner::Singleton { id: this_id } => {
335                if let TypeFamilyInner::Singleton { id: other_id } = &other.0 {
336                    return this_id.eq(other_id);
337                }
338
339                return false;
340            }
341
342            // Safety: Discriminant is checked.
343            TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
344
345            TypeFamilyInner::Reference { ptr } => *ptr,
346        };
347
348        let other_ptr = match &other.0 {
349            TypeFamilyInner::Singleton { .. } => return false,
350
351            // Safety: Discriminant is checked.
352            TypeFamilyInner::Group { .. } => unsafe { other.ptr() },
353
354            TypeFamilyInner::Reference { ptr } => *ptr,
355        };
356
357        this_ptr.eq(&other_ptr)
358    }
359}
360
361impl Eq for TypeFamily {}
362
363impl Hash for TypeFamily {
364    #[inline]
365    fn hash<H: Hasher>(&self, state: &mut H) {
366        let reference = match &self.0 {
367            TypeFamilyInner::Singleton { id } => return id.hash(state),
368
369            // Safety: Discriminant is checked.
370            TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
371
372            TypeFamilyInner::Reference { ptr } => *ptr,
373        };
374
375        reference.hash(state);
376    }
377}
378
379impl Display for TypeFamily {
380    #[inline]
381    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
382        let name = self.name();
383        formatter.write_str(name)?;
384
385        if !formatter.alternate() {
386            return Ok(());
387        }
388
389        formatter.write_str("(")?;
390
391        let mut first = true;
392        for ty in self {
393            match first {
394                true => first = false,
395                false => formatter.write_str(", ")?,
396            }
397
398            Display::fmt(ty.name, formatter)?;
399        }
400
401        formatter.write_str(")")?;
402
403        Ok(())
404    }
405}
406
407impl Debug for TypeFamily {
408    #[inline(always)]
409    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
410        Display::fmt(self, formatter)
411    }
412}
413
414impl<'a> IntoIterator for &'a TypeFamily {
415    type Item = &'static TypeMeta;
416    type IntoIter = TypeFamilyIter;
417
418    #[inline]
419    fn into_iter(self) -> Self::IntoIter {
420        let reference = match &self.0 {
421            TypeFamilyInner::Singleton { id } => return TypeFamilyIter::Singleton(*id),
422
423            // Safety: Discriminant is checked.
424            TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
425
426            TypeFamilyInner::Reference { ptr } => *ptr,
427        };
428
429        let registry = TypeRegistry::get();
430
431        match registry.family_index.get(&reference) {
432            None => TypeFamilyIter::Ended,
433            Some(set) => TypeFamilyIter::Group(set.iter()),
434        }
435    }
436}
437
438impl TypeFamily {
439    /// Creates a new TypeFamily with the specified `name` and without
440    /// documentation.
441    ///
442    /// Instances of type families should typically be stored in statics:
443    ///
444    /// ```
445    /// # use ad_astra::runtime::TypeFamily;
446    /// #
447    /// static FOO_FAMILY: TypeFamily = TypeFamily::new("foo");
448    /// ```
449    ///
450    /// It is recommended to use the [type_family](crate::type_family)
451    /// declarative macro to declare type families instead.
452    #[inline(always)]
453    pub const fn new(name: &'static str) -> Self {
454        Self(TypeFamilyInner::Group { name, doc: None })
455    }
456
457    /// Similar to the [new](Self::new) constructor, but allows specifying
458    /// RustDoc documentation for the type family through the `doc` parameter.
459    ///
460    /// The `doc` string is expected to be raw Markdown documentation text.
461    #[inline(always)]
462    pub const fn with_doc(name: &'static str, doc: &'static str) -> Self {
463        Self(TypeFamilyInner::Group {
464            name,
465            doc: Some(doc),
466        })
467    }
468
469    /// Returns a reference to the type family of the [Nil](TypeMeta::nil) type.
470    #[inline(always)]
471    pub fn nil() -> &'static Self {
472        TypeMeta::nil().family()
473    }
474
475    /// Returns a reference to the type family of the [dynamic](DynamicType)
476    /// type.
477    #[inline(always)]
478    pub fn dynamic() -> &'static Self {
479        TypeMeta::dynamic().family()
480    }
481
482    /// Returns a reference to the type family of function-like objects,
483    /// which are objects that have
484    /// [invocation](crate::runtime::Prototype::implements_invocation)
485    /// capabilities.
486    #[inline(always)]
487    pub fn fn_family() -> &'static Self {
488        &crate::runtime::__intrinsics::FUNCTION_FAMILY
489    }
490
491    /// Returns a reference to the type family of
492    /// [ScriptPackage](crate::runtime::ScriptPackage) objects.
493    #[inline(always)]
494    pub fn package() -> &'static Self {
495        &crate::runtime::__intrinsics::PACKAGE_FAMILY
496    }
497
498    /// Returns a reference to the type family of numeric objects.
499    ///
500    /// The [usize], [f32], and other Rust built-in numeric types belong to this
501    /// family.
502    #[inline(always)]
503    pub fn number() -> &'static Self {
504        &NUMBER_FAMILY
505    }
506
507    /// Returns true if this family is the [Nil Family](Self::nil).
508    #[inline(always)]
509    pub fn is_nil(&self) -> bool {
510        self == Self::nil()
511    }
512
513    /// Returns true if this family is the [Dynamic Family](Self::dynamic).
514    #[inline(always)]
515    pub fn is_dynamic(&self) -> bool {
516        self == Self::dynamic()
517    }
518
519    /// Returns true if this family is the [Functions Family](Self::fn_family).
520    #[inline(always)]
521    pub fn is_fn(&self) -> bool {
522        self == Self::fn_family()
523    }
524
525    /// Returns true if this family is the [Packages Family](Self::package).
526    #[inline(always)]
527    pub fn is_package(&self) -> bool {
528        self == Self::package()
529    }
530
531    /// Returns true if this family is the [Numeric Family](Self::number).
532    #[inline(always)]
533    pub fn is_number(&self) -> bool {
534        self == Self::number()
535    }
536
537    /// Returns the number of types associated with this family.
538    #[inline(always)]
539    pub fn len(&self) -> usize {
540        let reference = match &self.0 {
541            TypeFamilyInner::Singleton { .. } => return 1,
542
543            // Safety: Discriminant is checked.
544            TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
545
546            TypeFamilyInner::Reference { ptr } => *ptr,
547        };
548
549        let registry = TypeRegistry::get();
550
551        let Some(set) = registry.family_index.get(&reference) else {
552            return 0;
553        };
554
555        set.len()
556    }
557
558    /// Returns the user-facing name of this family.
559    #[inline(always)]
560    pub fn name(&self) -> &'static str {
561        match &self.0 {
562            TypeFamilyInner::Singleton { id } => {
563                let registry = TypeRegistry::get();
564
565                match registry.type_index.get(id) {
566                    Some(meta) => meta.name,
567
568                    // Safety: Singletons always refer existing TypeMeta entries.
569                    None => unsafe { debug_unreachable!("Missing singleton type family entry.") },
570                }
571            }
572
573            TypeFamilyInner::Group { name, .. } => *name,
574
575            TypeFamilyInner::Reference { ptr } => {
576                // Safety: References always point to existing static data.
577                let family = unsafe { ptr.as_ref() };
578
579                match family.0 {
580                    TypeFamilyInner::Group { name, .. } => name,
581
582                    // Safety: References always point to Groups.
583                    _ => unsafe { debug_unreachable!("TypeFamily broken reference.") },
584                }
585            }
586        }
587    }
588
589    /// Returns the RustDoc documentation for this type family. Returns None if
590    /// the family does not have specified documentation.
591    #[inline(always)]
592    pub fn doc(&self) -> Option<&'static str> {
593        match &self.0 {
594            TypeFamilyInner::Singleton { id } => {
595                let registry = TypeRegistry::get();
596
597                match registry.type_index.get(id) {
598                    Some(meta) => meta.doc,
599
600                    // Safety: Singletons always refer existing TypeMeta entries.
601                    None => unsafe { debug_unreachable!("Missing singleton type family entry.") },
602                }
603            }
604
605            TypeFamilyInner::Group { doc, .. } => *doc,
606
607            TypeFamilyInner::Reference { ptr } => {
608                // Safety: References always point to existing static data.
609                let family = unsafe { ptr.as_ref() };
610
611                match family.0 {
612                    TypeFamilyInner::Group { doc, .. } => doc,
613
614                    // Safety: References always point to Groups.
615                    _ => unsafe { debug_unreachable!("TypeFamily broken reference.") },
616                }
617            }
618        }
619    }
620
621    /// Returns true if this type family contains a Rust type with the
622    /// specified [TypeId].
623    #[inline(always)]
624    pub fn includes(&self, ty: &TypeId) -> bool {
625        let ptr = match &self.0 {
626            TypeFamilyInner::Singleton { id } => return id.eq(ty),
627
628            // Safety: Discriminant is checked.
629            TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
630
631            TypeFamilyInner::Reference { ptr } => *ptr,
632        };
633
634        let registry = TypeRegistry::get();
635
636        let set = match registry.family_index.get(&ptr) {
637            None => return false,
638            Some(set) => set,
639        };
640
641        set.contains(ty)
642    }
643
644    // Safety: Inner discriminant is Group.
645    unsafe fn ptr(&self) -> NonNull<TypeFamily> {
646        match &self.0 {
647            TypeFamilyInner::Group { .. } => unsafe {
648                NonNull::new_unchecked(self as *const TypeFamily as *mut TypeFamily)
649            },
650
651            // Safety: Upheld by the caller.
652            _ => unsafe {
653                debug_unreachable!("An attempt to crate pointer from non-Group TypeFamily.")
654            },
655        }
656    }
657}
658
659pub enum TypeFamilyIter {
660    Ended,
661    Singleton(TypeId),
662    Group(Iter<'static, TypeId>),
663}
664
665impl Iterator for TypeFamilyIter {
666    type Item = &'static TypeMeta;
667
668    #[inline]
669    fn next(&mut self) -> Option<Self::Item> {
670        match self {
671            Self::Ended => None,
672
673            Self::Singleton(id) => {
674                let id = *id;
675
676                *self = Self::Ended;
677
678                match TypeMeta::by_id(&id) {
679                    None => unsafe { debug_unreachable!("Invalid TypeFamily singleton.") },
680
681                    Some(meta) => Some(meta),
682                }
683            }
684
685            Self::Group(iterator) => match iterator.next() {
686                None => None,
687
688                Some(id) => match TypeMeta::by_id(&id) {
689                    None => unsafe { debug_unreachable!("Invalid TypeFamily group.") },
690
691                    Some(meta) => Some(meta),
692                },
693            },
694        }
695    }
696}
697
698impl FusedIterator for TypeFamilyIter {}
699
700#[derive(Copy)]
701enum TypeFamilyInner {
702    Singleton {
703        id: TypeId,
704    },
705    Group {
706        name: &'static str,
707        doc: Option<&'static str>,
708    },
709    Reference {
710        ptr: NonNull<TypeFamily>,
711    },
712}
713
714impl Debug for TypeFamilyInner {
715    #[inline(always)]
716    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
717        // Safety: Transparent type transmutation.
718        let this = unsafe { transmute::<&TypeFamilyInner, &TypeFamily>(self) };
719
720        Debug::fmt(this, formatter)
721    }
722}
723
724// Safety: The inner pointer refers static data which is Send+Sync.
725unsafe impl Send for TypeFamilyInner {}
726
727// Safety: The inner pointer refers static data which is Send+Sync.
728unsafe impl Sync for TypeFamilyInner {}
729
730impl Clone for TypeFamilyInner {
731    fn clone(&self) -> Self {
732        match self {
733            Self::Singleton { id } => Self::Singleton { id: *id },
734            Self::Reference { ptr } => Self::Reference { ptr: *ptr },
735
736            // Safety: This variant is never exposed to the public clone-able interface.
737            Self::Group { .. } => unsafe {
738                debug_unreachable!("An attempt to clone Group TypeFamily")
739            },
740        }
741    }
742}
743
744/// A macro that declares new [type families](TypeFamily).
745///
746/// Using this macro, you can declare new type families in statics,
747/// with RustDoc documentation using [TypeFamily::with_doc] or without
748/// it using [TypeFamily::new].
749///
750/// ```
751/// use ad_astra::type_family;
752///
753/// type_family!(
754///     /// Documentation line 1.
755///     /// Documentation line 2.
756///     pub static FOO_FAMILY = "foo";
757///
758///     static BAR_FAMILY = "bar";
759/// );
760///
761/// assert_eq!(FOO_FAMILY.name(), "foo");
762/// assert_eq!(FOO_FAMILY.doc(), Some(" Documentation line 1.\n Documentation line 2.\n"));
763///
764/// assert_eq!(BAR_FAMILY.name(), "bar");
765/// assert_eq!(BAR_FAMILY.doc(), None);
766/// ```
767#[macro_export]
768macro_rules! type_family {
769    (
770        $vis:vis static $ident:ident = $name:expr;
771    ) => {
772        $vis static $ident: $crate::runtime::TypeFamily = $crate::runtime::TypeFamily::new($name);
773    };
774
775    (
776        $(#[doc = $doc:expr])+
777        $vis:vis static $ident:ident = $name:expr;
778    ) => {
779        $(#[doc = $doc])+
780        $vis static $ident: $crate::runtime::TypeFamily = $crate::runtime::TypeFamily::with_doc(
781            $name, ::std::concat!($($doc, "\n"),+)
782        );
783    };
784
785    {
786        $(
787            $(#[doc = $doc:expr])*
788            $vis:vis static $ident:ident = $name:expr;
789        )*
790    } => {
791        $(
792            $crate::type_family!{
793                $(#[doc = $doc])*
794                $vis static $ident = $name;
795            }
796        )*
797    };
798}
799
800struct TypeRegistry {
801    type_index: AHashMap<TypeId, TypeMeta>,
802    family_index: AHashMap<NonNull<TypeFamily>, AHashSet<TypeId>>,
803}
804
805// Safety: The inner pointer refers static data which is Send+Sync.
806unsafe impl Send for TypeRegistry {}
807
808// Safety: The inner pointer refers static data which is Send+Sync.
809unsafe impl Sync for TypeRegistry {}
810
811impl TypeRegistry {
812    #[inline(always)]
813    fn get() -> &'static Self {
814        static REGISTRY: Lazy<TypeRegistry> = Lazy::new(|| {
815            let mut type_index = AHashMap::<TypeId, TypeMeta>::new();
816            let mut family_index = AHashMap::<NonNull<TypeFamily>, AHashSet<TypeId>>::new();
817
818            for group in DeclarationGroup::enumerate() {
819                let origin = group.origin;
820
821                for declaration in &group.type_metas {
822                    let declaration = declaration();
823
824                    if let Some(previous) = type_index.get(&declaration.id) {
825                        origin.blame(&format!(
826                            "Type {} already declared in {} as {}.",
827                            declaration.name, previous.origin, previous.name,
828                        ))
829                    }
830
831                    let family = match declaration.family {
832                        None => TypeFamilyInner::Singleton { id: declaration.id },
833
834                        Some(group) => {
835                            // Safety: By the time of the Registry creation
836                            //         there are instances of the Group TypeFamilies
837                            //         only available in the external context.
838                            let ptr = unsafe { group.ptr() };
839
840                            let set = family_index.entry(ptr).or_default();
841
842                            if !set.insert(declaration.id) {
843                                // Safety: Uniqueness checked above.
844                                unsafe { debug_unreachable!("Duplicate type family entry.") }
845                            }
846
847                            TypeFamilyInner::Reference { ptr }
848                        }
849                    };
850
851                    let meta = TypeMeta {
852                        id: declaration.id,
853                        name: declaration.name,
854                        origin,
855                        doc: declaration.doc,
856                        family,
857                        size: declaration.size,
858                    };
859
860                    if let Some(_) = type_index.insert(declaration.id, meta) {
861                        // Safety: Uniqueness checked above.
862                        unsafe { debug_unreachable!("Duplicate type meta entry.") }
863                    }
864                }
865            }
866
867            TypeRegistry {
868                type_index,
869                family_index,
870            }
871        });
872
873        REGISTRY.deref()
874    }
875}
876
877use crate::exports::NUMBER_FAMILY;