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;