diff --git a/libs/@local/hashql/ast/src/lower/expander/error.rs b/libs/@local/hashql/ast/src/lower/expander/error.rs index b6ec20e3eed..0a7ee3b1e7a 100644 --- a/libs/@local/hashql/ast/src/lower/expander/error.rs +++ b/libs/@local/hashql/ast/src/lower/expander/error.rs @@ -358,9 +358,19 @@ impl Display for FormatUserPath<'_, '_> { fn format_absolute_path<'heap>(item: &Item<'heap>, registry: &ModuleRegistry<'heap>) -> String { use core::iter; - let path = item.absolute_path(registry).into_iter().map(Symbol::unwrap); + #[expect( + clippy::needless_collect, + reason = "debugging code + required for reverse" + )] + let path: Vec<_> = item + .absolute_path_rev(registry) + .map(Symbol::unwrap) + .collect(); - iter::once("").chain(path).intersperse("::").collect() + iter::once("") + .chain(path.into_iter().rev()) + .intersperse("::") + .collect() } struct SpellingSuggestions<'heap, I> { diff --git a/libs/@local/hashql/core/src/lib.rs b/libs/@local/hashql/core/src/lib.rs index 3e5b5ffbeba..daf94f95779 100644 --- a/libs/@local/hashql/core/src/lib.rs +++ b/libs/@local/hashql/core/src/lib.rs @@ -38,7 +38,8 @@ step_trait, sync_nonpoison, try_trait_v2, - variant_count + variant_count, + maybe_uninit_fill )] extern crate alloc; diff --git a/libs/@local/hashql/core/src/module/item.rs b/libs/@local/hashql/core/src/module/item.rs index bb4906df8e3..9bdde1328da 100644 --- a/libs/@local/hashql/core/src/module/item.rs +++ b/libs/@local/hashql/core/src/module/item.rs @@ -76,6 +76,7 @@ pub struct Item<'heap> { } impl<'heap> Item<'heap> { + #[must_use] pub fn ancestors( &self, registry: &ModuleRegistry<'heap>, @@ -87,27 +88,14 @@ impl<'heap> Item<'heap> { return None; } - let module = registry.modules.index(next); + let module = registry.modules[next]; next = module.parent; Some(module) }) } - // TODO: deprecate - pub fn absolute_path( - &self, - registry: &ModuleRegistry<'heap>, - ) -> impl IntoIterator> { - let mut modules: Vec<_> = self.ancestors(registry).into_iter().collect(); - modules.reverse(); - - modules - .into_iter() - .map(|module| module.name) - .chain(iter::once(self.name)) - } - + #[must_use] pub fn absolute_path_rev( &self, registry: &ModuleRegistry<'heap>, diff --git a/libs/@local/hashql/core/src/module/mod.rs b/libs/@local/hashql/core/src/module/mod.rs index ad56ed14a94..987595f55f9 100644 --- a/libs/@local/hashql/core/src/module/mod.rs +++ b/libs/@local/hashql/core/src/module/mod.rs @@ -12,8 +12,7 @@ mod resolver; pub mod std_lib; pub mod universe; -use core::{num::NonZero, slice}; -use std::sync::RwLock; +use core::{alloc::Allocator, num::NonZero, slice}; use self::{ error::{ResolutionError, ResolutionSuggestion}, @@ -23,10 +22,9 @@ use self::{ }; pub use self::{resolver::Reference, universe::Universe}; use crate::{ - collections::{FastHashMap, FastHashSet}, - heap::Heap, - id::{HasId, Id as _, newtype}, - intern::{Decompose, InternMap, InternSet, Interned, Provisioned}, + collections::{FastHashMap, fast_hash_map_in}, + heap::{BumpAllocator as _, Heap}, + id::{HasId, Id as _, IdSlice, IdVec, bit_vec::DenseBitSet, newtype}, symbol::Symbol, r#type::environment::Environment, }; @@ -40,131 +38,178 @@ impl ModuleId { pub const ROOT: Self = Self::MAX; } -/// The central registry for all modules and items in a HashQL program. +/// A builder for constructing a [`ModuleRegistry`]. /// -/// The `ModuleRegistry` serves as the global namespace for module resolution. -/// It tracks all available modules and their exported items. -#[derive(Debug)] -pub struct ModuleRegistry<'heap> { - /// A reference to the global heap used for memory allocation. - pub heap: &'heap Heap, - - pub modules: InternMap<'heap, Module<'heap>>, - items: InternSet<'heap, [Item<'heap>]>, - - root: RwLock, ModuleId>>, +/// Collects module definitions during construction (e.g. stdlib registration) and produces +/// an immutable [`ModuleRegistry`] via [`finish`]. +/// +/// The typical construction sequence is: +/// 1. Create with [`new_in`] +/// 2. Provision module IDs with [`provision_module`] +/// 3. Insert fully built modules with [`insert_module`] +/// 4. Register root-level modules with [`register`] +/// 5. Finalize into a [`ModuleRegistry`] with [`finish`] +/// +/// [`new_in`]: Self::new_in +/// [`provision_module`]: Self::provision_module +/// [`insert_module`]: Self::insert_module +/// [`register`]: Self::register +/// [`finish`]: Self::finish +pub struct PartialModuleRegistry<'heap, S: Allocator> { + heap: &'heap Heap, + modules: IdVec>, S>, + + root: FastHashMap, ModuleId, &'heap Heap>, } -impl<'heap> ModuleRegistry<'heap> { - /// Creates an empty module registry using the given heap. - pub fn empty(heap: &'heap Heap) -> Self { +impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { + pub fn new_in(heap: &'heap Heap, scratch: S) -> Self { Self { heap, - modules: InternMap::new(heap), - items: InternSet::new(heap), - root: RwLock::default(), + modules: IdVec::new_in(scratch), + root: fast_hash_map_in(heap), } } - /// Creates a new module registry with the standard library pre-loaded. - /// - /// This initializes the registry with all the standard modules and items - /// defined in the standard library. - pub fn new(env: &Environment<'heap>) -> Self { - let this = Self::empty(env.heap); - - let mut std = StandardLibrary::new(env, &this); - std.register(); - - this + /// Reserves a fresh [`ModuleId`] that can be referenced by child items before the + /// module itself is inserted. + pub fn provision_module(&mut self) -> ModuleId { + self.modules.push(None) } - /// Interns a new module into the registry. + /// Inserts a fully constructed module into the registry. + /// + /// The module's `id` must have been previously obtained from [`provision_module`]. /// /// # Panics /// - /// In debug builds, this function will panic if any item in the module has a parent - /// that doesn't match the module ID. - pub fn intern_module( - &self, - closure: impl FnOnce(Provisioned) -> PartialModule<'heap>, - ) -> ModuleId { - self.modules - .intern(|id| { - let module = closure(id); - - if cfg!(debug_assertions) { - for item in module.items { - assert_eq!(item.module, id.value()); - - // check for modules if the parent is also set *correctly* to our module - if let ItemKind::Module(child) = item.kind { - let child = self.modules.index(child); - - assert_eq!(child.parent, id.value()); - assert_eq!(child.depth.get(), module.depth.get() + 1); - assert_eq!(child.name, item.name); - } - } + /// In debug builds, panics if any item in the module references a different module ID + /// than the one being inserted, or if child module metadata is inconsistent. + /// + /// [`provision_module`]: Self::provision_module + pub fn insert_module(&mut self, module: Module<'heap>) { + #[cfg(debug_assertions)] + { + for item in module.items { + assert_eq!(item.module, module.id); + + // check for modules if the parent is also set *correctly* to our module + if let ItemKind::Module(child) = item.kind { + let child = self + .modules + .lookup(child) + .expect("child modules should be registered before their parents"); + + assert_eq!(child.parent, module.id); + assert_eq!(child.depth.get(), module.depth.get() + 1); + assert_eq!(child.name, item.name); } + } + } - module - }) - .id + let value = self.modules.insert(module.id, module); + debug_assert!(value.is_none()); } - /// Interns a slice of items into the registry. - pub fn intern_items(&self, items: &[Item<'heap>]) -> Interned<'heap, [Item<'heap>]> { - self.items.intern_slice(items) + /// Registers a module in the root namespace, making it accessible by name. + /// + /// # Panics + /// + /// Panics if the module has not been inserted via [`insert_module`]. + /// + /// [`insert_module`]: Self::insert_module + pub fn register(&mut self, module: ModuleId) { + let module = self + .modules + .lookup(module) + .expect("module must be inserted to be able to register it"); + + debug_assert_eq!(module.parent, ModuleId::ROOT); + + self.root.insert(module.name, module.id); } - /// Register a new module in the root namespace. + /// Finalizes the builder into an immutable [`ModuleRegistry`]. /// /// # Panics /// - /// This function will panic if the internal `RwLock` is poisoned. - pub fn register(&self, module: ModuleId) { - let module = self.modules.index(module); + /// Panics if any provisioned module ID was never populated via [`insert_module`]. + /// + /// [`insert_module`]: Self::insert_module + #[expect(unsafe_code)] + pub fn finish(self) -> ModuleRegistry<'heap> { + assert!( + self.modules.iter().all(Option::is_some), + "all modules must be inserted to be able to finish the registry" + ); - if cfg!(debug_assertions) { - assert_eq!(module.parent, ModuleId::ROOT); + let modules = self.heap.allocate_slice_uninit(self.modules.len()); + for (dst, src) in modules.iter_mut().zip(self.modules.iter()) { + // SAFETY: We have just verified above that all modules are Some + unsafe { + dst.write(src.unwrap_unchecked()); + } } - let mut root = self.root.write().expect("lock should not be poisoned"); - root.insert(module.name, module.id); - drop(root); + // SAFETY: We have just written all items into the slice, and have verified above that all + // modules are Some + let modules = unsafe { modules.assume_init_ref() }; + + ModuleRegistry { + heap: self.heap, + modules: IdSlice::from_raw(modules), + root: self.root, + } } +} - /// Find an item by name in the root namespace. - /// - /// # Panics +/// The central registry for all modules and items in a HashQL program. +/// +/// The `ModuleRegistry` serves as the global namespace for module resolution. +/// It tracks all available modules and their exported items. +#[derive(Debug)] +pub struct ModuleRegistry<'heap> { + /// A reference to the global heap used for memory allocation. + pub heap: &'heap Heap, + + modules: &'heap IdSlice>, + + root: FastHashMap, ModuleId, &'heap Heap>, +} + +impl<'heap> ModuleRegistry<'heap> { + /// Creates a new module registry with the standard library pre-loaded. /// - /// This function will panic if the internal `RwLock` is poisoned. - pub fn find_by_name(&self, name: Symbol<'heap>) -> Option> { - let root = self.root.read().expect("lock should not be poisoned"); + /// This initializes the registry with all the standard modules and items + /// defined in the standard library, using the global allocator for scratch space. + pub fn new(env: &Environment<'heap>) -> Self { + Self::new_in(env, alloc::alloc::Global) + } - let id = root.get(&name).copied()?; - drop(root); + /// Creates a new module registry with the standard library pre-loaded, using `scratch` + /// for temporary allocations during construction. + pub fn new_in(env: &Environment<'heap>, scratch: S) -> Self { + let mut partial = PartialModuleRegistry::new_in(env.heap, scratch.clone()); - let module = self.modules.index(id); + let mut std = StandardLibrary::new(env, &mut partial, scratch); + std.register(); - Some(module) + partial.finish() } - /// Finds suggestions for the given name in the root namespace. - fn suggestions(&self) -> Vec> { - let root = self.root.read().expect("lock should not be poisoned"); - - let mut results = Vec::with_capacity(root.len()); - for (&key, &module) in &*root { - results.push(ResolutionSuggestion { - item: module, - name: key, - }); - } - drop(root); + /// Looks up a root-level module by name. + #[must_use] + pub fn find_by_name(&self, name: Symbol<'heap>) -> Option> { + let id = self.root.get(&name).copied()?; + + Some(self.modules[id]) + } - results + /// Finds suggestions for the given name in the root namespace. + fn suggestions(&self) -> impl ExactSizeIterator> { + self.root + .iter() + .map(|(&name, &id)| ResolutionSuggestion { item: id, name }) } /// Resolves a path to an item in the registry. @@ -277,27 +322,15 @@ impl<'heap> ModuleRegistry<'heap> { /// This method requires allocation for the traversal state: /// - A vector for the module exploration stack /// - A hash set for tracking visited modules - /// - /// # Panics - /// - /// This function will panic if the internal `RwLock` is poisoned. + #[must_use] pub fn search_by_name( &self, name: Symbol<'heap>, universe: Universe, ) -> impl IntoIterator> { - let mut stack: Vec<_> = self - .root - .read() - .expect("lock should not be poisoned") - .values() - .copied() - .collect(); - - let mut seen = FastHashSet::with_capacity_and_hasher( - stack.len(), - foldhash::fast::RandomState::default(), - ); + let mut stack: Vec<_> = self.root.values().copied().collect(); + + let mut seen = DenseBitSet::new_empty(self.modules.len()); let mut current: slice::Iter<'heap, Item<'heap>> = [].iter(); core::iter::from_fn(move || { @@ -309,7 +342,7 @@ impl<'heap> ModuleRegistry<'heap> { } if let ItemKind::Module(child) = item.kind - && !seen.contains(&child) + && !seen.contains(child) { stack.push(child); } @@ -317,14 +350,11 @@ impl<'heap> ModuleRegistry<'heap> { // Current module is exhausted, try to get the next module from the stack while let Some(id) = stack.pop() { - if seen.contains(&id) { + if !seen.insert(id) { continue; } - seen.insert(id); - - let module = self.modules.index(id); - current = module.items.into_iter(); + current = self.modules[id].items.iter(); // Jump back to processing items in this new module continue 'outer; @@ -338,14 +368,13 @@ impl<'heap> ModuleRegistry<'heap> { }) } + #[must_use] pub fn module_depth(&self, id: ModuleId) -> u32 { if id == ModuleId::ROOT { return 0; } - let module = self.modules.index(id); - - module.depth.get() + self.modules[id].depth.get() } } @@ -362,7 +391,7 @@ pub struct Module<'heap> { pub parent: ModuleId, pub depth: NonZero, - pub items: Interned<'heap, [Item<'heap>]>, + pub items: &'heap [Item<'heap>], } impl<'heap> Module<'heap> { @@ -387,32 +416,56 @@ impl<'heap> Module<'heap> { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct PartialModule<'heap> { - name: Symbol<'heap>, - parent: ModuleId, - depth: NonZero, - items: Interned<'heap, [Item<'heap>]>, +impl HasId for Module<'_> { + type Id = ModuleId; + + fn id(&self) -> Self::Id { + self.id + } } -impl<'heap> Decompose<'heap> for Module<'heap> { - type Partial = PartialModule<'heap>; +#[cfg(test)] +impl<'heap> ModuleRegistry<'heap> { + /// Creates a partially built registry with the stdlib loaded. + /// + /// Returns a `PartialModuleRegistry` that can be further customized (e.g. by inserting + /// test modules) before calling [`PartialModuleRegistry::finish`]. + pub(crate) fn builder( + env: &Environment<'heap>, + ) -> PartialModuleRegistry<'heap, alloc::alloc::Global> { + use alloc::alloc::Global; - fn from_parts(id: Self::Id, partial: Interned<'heap, Self::Partial>) -> Self { - Self { - id, - name: partial.name, - parent: partial.parent, - depth: partial.depth, - items: partial.items, - } + let mut partial = PartialModuleRegistry::new_in(env.heap, Global); + + let mut std = StandardLibrary::new(env, &mut partial, Global); + std.register(); + + partial } } -impl HasId for Module<'_> { - type Id = ModuleId; +#[cfg(test)] +impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { + /// Inserts a root-level module with the given name and items. + /// + /// This is a test helper that provisions a module ID, passes it to the `items` closure + /// so items can reference their owning module, then inserts and registers the module. + pub(crate) fn insert_root_module( + &mut self, + name: Symbol<'heap>, + items: impl FnOnce(ModuleId) -> &'heap [Item<'heap>], + ) -> ModuleId { + let id = self.provision_module(); - fn id(&self) -> Self::Id { - self.id + self.insert_module(Module { + id, + name, + parent: ModuleId::ROOT, + depth: const { NonZero::new(1).unwrap() }, + items: items(id), + }); + self.register(id); + + id } } diff --git a/libs/@local/hashql/core/src/module/namespace.rs b/libs/@local/hashql/core/src/module/namespace.rs index 072e0a91a35..68467edb9a9 100644 --- a/libs/@local/hashql/core/src/module/namespace.rs +++ b/libs/@local/hashql/core/src/module/namespace.rs @@ -53,6 +53,7 @@ pub struct ModuleNamespace<'env, 'heap> { impl<'env, 'heap> ModuleNamespace<'env, 'heap> { /// Create a new module namespace. + #[must_use] pub fn new(registry: &'env ModuleRegistry<'heap>) -> Self { Self { registry, @@ -312,13 +313,9 @@ impl<'env, 'heap> ModuleNamespace<'env, 'heap> { } fn import_modules(&mut self) { - let root = self - .registry - .root - .read() - .expect("should be able to lock registry"); + let root = &self.registry.root; - for (&name, &module) in &*root { + for (&name, &module) in root { self.imports.push(Import { name, item: ImportReference::Item(Item { @@ -328,8 +325,6 @@ impl<'env, 'heap> ModuleNamespace<'env, 'heap> { }), }); } - - drop(root); } /// Imports all standard prelude items and all root modules. @@ -480,13 +475,13 @@ impl<'env, 'heap> ModuleNamespace<'env, 'heap> { #[cfg(test)] mod tests { #![coverage(off)] - use core::{assert_matches, num::NonZero}; + use core::assert_matches; use super::ModuleNamespace; use crate::{ - heap::Heap, + heap::{BumpAllocator as _, Heap}, module::{ - ModuleId, ModuleRegistry, PartialModule, Reference, Universe, + ModuleRegistry, Reference, Universe, error::ResolutionError, item::{IntrinsicItem, IntrinsicTypeItem, IntrinsicValueItem, Item, ItemKind}, namespace::{ImportOptions, ResolutionMode, ResolveOptions}, @@ -683,25 +678,21 @@ mod tests { fn shadowed_import() { let heap = Heap::new(); let environment = Environment::new(&heap); - let registry = ModuleRegistry::new(&environment); - - let mut namespace = ModuleNamespace::new(®istry); - namespace.import_prelude(); - - let module = registry.intern_module(|id| PartialModule { - parent: ModuleId::ROOT, - depth: const { NonZero::new(1).unwrap() }, - name: heap.intern_symbol("foo"), - items: registry.intern_items(&[Item { - module: id.value(), + let mut builder = ModuleRegistry::builder(&environment); + builder.insert_root_module(heap.intern_symbol("foo"), |id| { + heap.allocate_slice_copy(&[Item { + module: id, name: heap.intern_symbol("bar"), kind: ItemKind::Intrinsic(IntrinsicItem::Type(IntrinsicTypeItem { name: heap.intern_symbol("::foo::bar"), })), - }]), + }]) }); - registry.register(module); + let registry = builder.finish(); + + let mut namespace = ModuleNamespace::new(®istry); + namespace.import_prelude(); let import = namespace .resolve_relative( @@ -953,7 +944,22 @@ mod tests { fn alias_shadows_import() { let heap = Heap::new(); let environment = Environment::new(&heap); - let registry = ModuleRegistry::new(&environment); + + let mut builder = ModuleRegistry::builder(&environment); + let custom_module = builder.insert_root_module(heap.intern_symbol("custom"), |id| { + heap.allocate_slice_copy(&[Item { + module: id, + name: heap.intern_symbol("my_add"), + kind: ItemKind::Intrinsic(IntrinsicItem::Value(IntrinsicValueItem { + name: heap.intern_symbol("::custom::my_add"), + r#type: crate::module::locals::TypeDef { + id: crate::r#type::builder::TypeBuilder::synthetic(&environment).never(), + arguments: crate::intern::Interned::empty(), + }, + })), + }]) + }); + let registry = builder.finish(); let mut namespace = ModuleNamespace::new(®istry); namespace.import_prelude(); @@ -972,36 +978,9 @@ mod tests { assert_eq!(original.name.as_str(), "add"); - // Create a custom item and alias `+` to it - let ItemKind::Intrinsic(IntrinsicItem::Value(IntrinsicValueItem { - r#type: original_type, - .. - })) = original.kind - else { - panic!("expected intrinsic value item"); - }; - - let custom_module = registry.intern_module(|id| PartialModule { - parent: ModuleId::ROOT, - depth: const { NonZero::new(1).unwrap() }, - name: heap.intern_symbol("custom"), - - items: registry.intern_items(&[Item { - module: id.value(), - name: heap.intern_symbol("my_add"), - kind: ItemKind::Intrinsic(IntrinsicItem::Value(IntrinsicValueItem { - name: heap.intern_symbol("::custom::my_add"), - r#type: original_type, - })), - }]), - }); - registry.register(custom_module); - - let custom_item = *registry - .modules - .index(custom_module) + let custom_item = *registry.modules[custom_module] .items - .into_iter() + .iter() .next() .expect("module should have one item"); diff --git a/libs/@local/hashql/core/src/module/resolver.rs b/libs/@local/hashql/core/src/module/resolver.rs index 1dd95181553..7944bf057d0 100644 --- a/libs/@local/hashql/core/src/module/resolver.rs +++ b/libs/@local/hashql/core/src/module/resolver.rs @@ -152,7 +152,7 @@ impl<'heap> Resolver<'_, 'heap> { ) -> Result, ResolutionError<'heap>> { let mut item = module .items - .into_iter() + .iter() .copied() .filter(move |item| item.name == name) .map(Reference::Item) @@ -200,13 +200,13 @@ impl<'heap> Resolver<'_, 'heap> { }); }; - let module = self.registry.modules.index(module); + let module = self.registry.modules[module]; if module.items.is_empty() { return Err(ResolutionError::ModuleEmpty { depth }); } - Ok(module.items.into_iter().copied().map(Reference::Item)) + Ok(module.items.iter().copied().map(Reference::Item)) } #[define_opaque(ModuleItemIterator)] @@ -225,7 +225,7 @@ impl<'heap> Resolver<'_, 'heap> { } return Ok(ResolveIter::Glob( - module.items.into_iter().copied().map(Reference::Item), + module.items.iter().copied().map(Reference::Item), )); } @@ -261,7 +261,7 @@ impl<'heap> Resolver<'_, 'heap> { }); }; - module = self.registry.modules.index(next); + module = self.registry.modules[next]; }; match self.options.mode { @@ -291,7 +291,7 @@ impl<'heap> Resolver<'_, 'heap> { return Err(ResolutionError::PackageNotFound { depth: 0, name, - suggestions: self.suggest(|| self.registry.suggestions()), + suggestions: self.suggest(|| self.registry.suggestions().collect()), }); }; @@ -444,13 +444,13 @@ impl<'heap> Resolver<'_, 'heap> { }); }; - let module = self.registry.modules.index(module); + let module = self.registry.modules[module]; if module.items.is_empty() { return Err(ResolutionError::ModuleEmpty { depth: 0 }); } - Ok(module.items.into_iter().copied().map(Reference::Item)) + Ok(module.items.iter().copied().map(Reference::Item)) } #[expect(clippy::panic_in_result_fn, reason = "sanity check")] @@ -503,7 +503,7 @@ impl<'heap> Resolver<'_, 'heap> { }); }; - let module = self.registry.modules.index(module); + let module = self.registry.modules[module]; return self.resolve_impl(query, module); } @@ -526,13 +526,13 @@ impl<'heap> Resolver<'_, 'heap> { #[cfg(test)] mod test { #![coverage(off)] - use core::{assert_matches, num::NonZero}; + use core::assert_matches; use super::{Reference, ResolutionError}; use crate::{ heap::Heap, module::{ - ModuleId, ModuleRegistry, PartialModule, Universe, + ModuleRegistry, Universe, item::Item, namespace::{ImportOptions, ModuleNamespace, ResolutionMode}, resolver::{Resolver, ResolverMode, ResolverOptions}, @@ -889,19 +889,10 @@ mod test { fn module_empty_error() { let heap = Heap::new(); let env = Environment::new(&heap); - let registry = ModuleRegistry::new(&env); - - // Create an empty module and register it - let empty_module_id = registry.intern_module(|_| { - PartialModule { - name: heap.intern_symbol("empty_module"), - parent: ModuleId::ROOT, - depth: const { NonZero::new(1).unwrap() }, - items: registry.intern_items(&[]), // No items - } - }); - registry.register(empty_module_id); + let mut builder = ModuleRegistry::builder(&env); + builder.insert_root_module(heap.intern_symbol("empty_module"), |_| &[]); + let registry = builder.finish(); let resolver = Resolver { registry: ®istry, diff --git a/libs/@local/hashql/core/src/module/std_lib/core/bits.rs b/libs/@local/hashql/core/src/module/std_lib/core/bits.rs index 4e8d7028f00..37a1378ea1f 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/bits.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/bits.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleDef, StandardLibrary, StandardLibraryModule, decl}, + std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl, + }, }, symbol::{Symbol, sym}, }; @@ -14,48 +18,53 @@ pub(in crate::module::std_lib) struct Bits { impl<'heap> StandardLibraryModule<'heap> for Bits { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreBits; + fn name() -> Symbol<'heap> { sym::bits } #[expect(non_snake_case)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let Integer = lib.ty.integer(); + let Integer = context.ty.integer(); let items = [ ( sym::path::core::bits::and, &[sym::and, sym::symbol::ampersand], - decl!(lib; <>(lhs: Integer, rhs: Integer) -> Integer), + decl!(context; <>(lhs: Integer, rhs: Integer) -> Integer), ), ( sym::path::core::bits::or, &[sym::or, sym::symbol::pipe], - decl!(lib; <>(lhs: Integer, rhs: Integer) -> Integer), + decl!(context; <>(lhs: Integer, rhs: Integer) -> Integer), ), ( sym::path::core::bits::xor, &[sym::xor, sym::symbol::caret], - decl!(lib; <>(lhs: Integer, rhs: Integer) -> Integer), + decl!(context; <>(lhs: Integer, rhs: Integer) -> Integer), ), ( sym::path::core::bits::not, &[sym::not, sym::symbol::tilde], - decl!(lib; <>(value: Integer) -> Integer), + decl!(context; <>(value: Integer) -> Integer), ), ( sym::path::core::bits::shl, &[sym::shl, sym::symbol::ltlt], // In the future we might want to specialize the `shift` to `Natural` - decl!(lib; <>(value: Integer, shift: Integer) -> Integer), + decl!(context; <>(value: Integer, shift: Integer) -> Integer), ), ( sym::path::core::bits::shr, &[sym::shr, sym::symbol::gtgt], // In the future we might want to specialize the `shift` to `Natural` - decl!(lib; <>(value: Integer, shift: Integer) -> Integer), + decl!(context; <>(value: Integer, shift: Integer) -> Integer), ), ]; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/bool.rs b/libs/@local/hashql/core/src/module/std_lib/core/bool.rs index 063e24345d7..ba388f2e19a 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/bool.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/bool.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleDef, StandardLibrary, StandardLibraryModule, decl}, + std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl, + }, }, symbol::{Symbol, sym}, }; @@ -14,31 +18,36 @@ pub(in crate::module::std_lib) struct Bool { impl<'heap> StandardLibraryModule<'heap> for Bool { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreBool; + fn name() -> Symbol<'heap> { sym::bool } #[expect(non_snake_case)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let Boolean = lib.ty.boolean(); + let Boolean = context.ty.boolean(); let items = [ ( sym::path::core::bool::not, &[sym::not, sym::symbol::exclamation], - decl!(lib; <>(value: Boolean) -> Boolean), + decl!(context; <>(value: Boolean) -> Boolean), ), ( sym::path::core::bool::and, &[sym::and, sym::symbol::ampamp], - decl!(lib; <>(lhs: Boolean, rhs: Boolean) -> Boolean), + decl!(context; <>(lhs: Boolean, rhs: Boolean) -> Boolean), ), ( sym::path::core::bool::or, &[sym::or, sym::symbol::pipepipe], - decl!(lib; <>(lhs: Boolean, rhs: Boolean) -> Boolean), + decl!(context; <>(lhs: Boolean, rhs: Boolean) -> Boolean), ), ]; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs b/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs index 23a2d5ab736..614d9a72957 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleDef, StandardLibrary, StandardLibraryModule, decl}, + std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl, + }, }, symbol::{Symbol, sym}, }; @@ -14,47 +18,52 @@ pub(in crate::module::std_lib) struct Cmp { impl<'heap> StandardLibraryModule<'heap> for Cmp { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreCmp; + fn name() -> Symbol<'heap> { sym::cmp } #[expect(non_snake_case)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let Number = lib.ty.number(); - let Boolean = lib.ty.boolean(); + let Number = context.ty.number(); + let Boolean = context.ty.boolean(); let items = [ ( sym::path::core::cmp::gt, &[sym::gt, sym::symbol::gt], - decl!(lib; <>(lhs: Number, rhs: Number) -> Boolean), + decl!(context; <>(lhs: Number, rhs: Number) -> Boolean), ), ( sym::path::core::cmp::lt, &[sym::lt, sym::symbol::lt], - decl!(lib; <>(lhs: Number, rhs: Number) -> Boolean), + decl!(context; <>(lhs: Number, rhs: Number) -> Boolean), ), ( sym::path::core::cmp::gte, &[sym::gte, sym::symbol::gteq], - decl!(lib; <>(lhs: Number, rhs: Number) -> Boolean), + decl!(context; <>(lhs: Number, rhs: Number) -> Boolean), ), ( sym::path::core::cmp::lte, &[sym::lte, sym::symbol::lteq], - decl!(lib; <>(lhs: Number, rhs: Number) -> Boolean), + decl!(context; <>(lhs: Number, rhs: Number) -> Boolean), ), ( sym::path::core::cmp::eq, &[sym::eq, sym::symbol::eqeq], - decl!(lib; (lhs: T, rhs: U) -> Boolean), + decl!(context; (lhs: T, rhs: U) -> Boolean), ), ( sym::path::core::cmp::ne, &[sym::ne, sym::symbol::excleq], - decl!(lib; (lhs: T, rhs: U) -> Boolean), + decl!(context; (lhs: T, rhs: U) -> Boolean), ), ]; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/json.rs b/libs/@local/hashql/core/src/module/std_lib/core/json.rs index 17383585dcd..741ff60b20d 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/json.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/json.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -10,27 +14,33 @@ pub(in crate::module::std_lib) struct Json { impl<'heap> StandardLibraryModule<'heap> for Json { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreJson; + fn name() -> Symbol<'heap> { sym::json } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); - let heap = lib.heap; + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // type JsonPathSegment = String | Integer; // Note: The type should be Natural instead, but this requires refinement types - let json_path_segment_ty = lib.ty.union([lib.ty.string(), lib.ty.integer()]); + let json_path_segment_ty = context + .ty + .union([context.ty.string(), context.ty.integer()]); def.push( - heap.intern_symbol("JsonPathSegment"), - ItemDef::r#type(lib.ty.env, json_path_segment_ty, &[]), + sym::JsonPathSegment, + ItemDef::r#type(context.ty.env, json_path_segment_ty, &[]), ); // type JsonPath = JsonPathSegment[]; - let json_path_ty = lib.ty.list(json_path_segment_ty); + let json_path_ty = context.ty.list(json_path_segment_ty); def.push( - heap.intern_symbol("JsonPath"), - ItemDef::r#type(lib.ty.env, json_path_ty, &[]), + sym::JsonPath, + ItemDef::r#type(context.ty.env, json_path_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/core/math.rs b/libs/@local/hashql/core/src/module/std_lib/core/math.rs index 2690ca0715b..f4e25bed6ed 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/math.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/math.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleDef, StandardLibrary, StandardLibraryModule, decl}, + std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl, + }, }, symbol::{Symbol, sym}, }; @@ -14,47 +18,52 @@ pub(in crate::module::std_lib) struct Math { impl<'heap> StandardLibraryModule<'heap> for Math { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreMath; + fn name() -> Symbol<'heap> { sym::math } #[expect(non_snake_case)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let Number = lib.ty.number(); - let Integer = lib.ty.integer(); + let Number = context.ty.number(); + let Integer = context.ty.integer(); let items = [ ( sym::path::core::math::add, &[sym::add, sym::symbol::plus] as &[Symbol<'heap>], - decl!(lib; (lhs: T, rhs: U) -> lib.ty.union([T, U])), + decl!(context; (lhs: T, rhs: U) -> context.ty.union([T, U])), ), ( sym::path::core::math::sub, &[sym::sub, sym::symbol::minus], - decl!(lib; (lhs: T, rhs: U) -> lib.ty.union([T, U])), + decl!(context; (lhs: T, rhs: U) -> context.ty.union([T, U])), ), ( sym::path::core::math::mul, &[sym::mul, sym::symbol::asterisk], - decl!(lib; (lhs: T, rhs: U) -> lib.ty.union([T, U])), + decl!(context; (lhs: T, rhs: U) -> context.ty.union([T, U])), ), ( sym::path::core::math::div, &[sym::div, sym::symbol::slash], - decl!(lib; <>(dividend: Number, divisor: Number) -> Number), + decl!(context; <>(dividend: Number, divisor: Number) -> Number), ), ( sym::path::core::math::rem, &[sym::rem, sym::symbol::percent], - decl!(lib; <>(dividend: Integer, divisor: Integer) -> Integer), + decl!(context; <>(dividend: Integer, divisor: Integer) -> Integer), ), ( sym::path::core::math::r#mod, &[sym::r#mod], - decl!(lib; <>(value: Integer, modulus: Integer) -> Integer), + decl!(context; <>(value: Integer, modulus: Integer) -> Integer), ), ( sym::path::core::math::pow, @@ -64,22 +73,22 @@ impl<'heap> StandardLibraryModule<'heap> for Math { sym::symbol::upwards, ], // (cannot be `Integer` on return, as `exponent` can be a negative integer) - decl!(lib; <>(base: Number, exponent: Number) -> Number), + decl!(context; <>(base: Number, exponent: Number) -> Number), ), ( sym::path::core::math::sqrt, &[sym::sqrt, sym::symbol::sqrt], - decl!(lib; <>(value: Number) -> Number), + decl!(context; <>(value: Number) -> Number), ), ( sym::path::core::math::cbrt, &[sym::cbrt, sym::symbol::cbrt], - decl!(lib; <>(value: Number) -> Number), + decl!(context; <>(value: Number) -> Number), ), ( sym::path::core::math::root, &[sym::root], // cannot use `ⁿ√` because `ⁿ` is a letter, not a symbol - decl!(lib; <>(value: Number, root: Number) -> Number), + decl!(context; <>(value: Number, root: Number) -> Number), ), ]; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/mod.rs b/libs/@local/hashql/core/src/module/std_lib/core/mod.rs index 968dcda1593..1c89a5fce56 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/mod.rs @@ -1,4 +1,8 @@ -use super::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}; +use core::alloc::Allocator; + +use super::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, +}; use crate::{ module::{item::IntrinsicValueItem, locals::TypeDef}, symbol::{Symbol, sym}, @@ -14,8 +18,8 @@ pub(in crate::module::std_lib) mod result; pub mod url; pub mod uuid; -pub(in crate::module::std_lib) fn func<'heap>( - def: &mut ModuleDef<'heap>, +pub(in crate::module::std_lib) fn func<'heap, S: Allocator>( + def: &mut ModuleDef<'heap, S>, path: Symbol<'heap>, names: impl IntoIterator>, @@ -42,11 +46,16 @@ impl<'heap> StandardLibraryModule<'heap> for Core { self::uuid::Uuid, ); + const CACHE_ID: CacheId = CacheId::Core; + fn name() -> Symbol<'heap> { sym::core } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/option.rs b/libs/@local/hashql/core/src/module/std_lib/core/option.rs index 1075b8ae7e3..7e0c1e687b9 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/option.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/option.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -26,34 +30,42 @@ pub(in crate::module::std_lib) struct Option { impl<'heap> StandardLibraryModule<'heap> for Option { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreOption; + fn name() -> Symbol<'heap> { sym::option } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // Option is simply a union between two opaque types, when the constructor only takes a // `Null` the constructor automatically allows for no-value. - let t_arg = lib.ty.fresh_argument(sym::T); - let t_ref = lib.ty.hydrate_argument(t_arg); - let t_param = lib.ty.param(t_arg); + let t_arg = context.ty.fresh_argument(sym::T); + let t_ref = context.ty.hydrate_argument(t_arg); + let t_param = context.ty.param(t_arg); // newtype None = Null; - let none_ty = lib.ty.opaque(sym::path::None, lib.ty.null()); - def.push(sym::None, ItemDef::newtype(lib.ty.env, none_ty, &[])); + let none_ty = context.ty.opaque(sym::path::None, context.ty.null()); + def.push(sym::None, ItemDef::newtype(context.ty.env, none_ty, &[])); // newtype Some = T; - let some_ty = lib + let some_ty = context .ty - .generic([(t_arg, None)], lib.ty.opaque(sym::path::Some, t_param)); - def.push(sym::Some, ItemDef::newtype(lib.ty.env, some_ty, &[t_ref])); + .generic([(t_arg, None)], context.ty.opaque(sym::path::Some, t_param)); + def.push( + sym::Some, + ItemDef::newtype(context.ty.env, some_ty, &[t_ref]), + ); // type Option = Some | None; - let option_ty = lib.ty.union([some_ty, none_ty]); + let option_ty = context.ty.union([some_ty, none_ty]); def.push( sym::Option, - ItemDef::r#type(lib.ty.env, option_ty, &[t_ref]), + ItemDef::r#type(context.ty.env, option_ty, &[t_ref]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/core/result.rs b/libs/@local/hashql/core/src/module/std_lib/core/result.rs index 91018546d28..b337b6129d7 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/result.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/result.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -10,46 +14,45 @@ pub(in crate::module::std_lib) struct Result { impl<'heap> StandardLibraryModule<'heap> for Result { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreResult; + fn name() -> Symbol<'heap> { sym::result } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let t_arg = lib.ty.fresh_argument("T"); - let t_ref = lib.ty.hydrate_argument(t_arg); - let t_param = lib.ty.param(t_arg); + let t_arg = context.ty.fresh_argument(sym::T); + let t_ref = context.ty.hydrate_argument(t_arg); + let t_param = context.ty.param(t_arg); - let e_arg = lib.ty.fresh_argument("E"); - let e_ref = lib.ty.hydrate_argument(e_arg); - let e_param = lib.ty.param(e_arg); + let e_arg = context.ty.fresh_argument(sym::E); + let e_ref = context.ty.hydrate_argument(e_arg); + let e_param = context.ty.param(e_arg); // newtype Ok = T; - let ok_ty = lib.ty.generic( + let ok_ty = context.ty.generic( [(t_arg, None)], - lib.ty.opaque("::core::result::Ok", t_param), - ); - def.push( - lib.heap.intern_symbol("Ok"), - ItemDef::newtype(lib.ty.env, ok_ty, &[t_ref]), + context.ty.opaque(sym::path::core::result::Ok, t_param), ); + def.push(sym::Ok, ItemDef::newtype(context.ty.env, ok_ty, &[t_ref])); // newtype Err = E; - let err_ty = lib.ty.generic( + let err_ty = context.ty.generic( [(e_arg, None)], - lib.ty.opaque("::core::result::Err", e_param), - ); - def.push( - lib.heap.intern_symbol("Err"), - ItemDef::newtype(lib.ty.env, err_ty, &[e_ref]), + context.ty.opaque(sym::path::core::result::Err, e_param), ); + def.push(sym::Err, ItemDef::newtype(context.ty.env, err_ty, &[e_ref])); // type Result = Ok | Err; - let result_ty = lib.ty.union([ok_ty, err_ty]); + let result_ty = context.ty.union([ok_ty, err_ty]); def.push( - lib.heap.intern_symbol("Result"), - ItemDef::newtype(lib.ty.env, result_ty, &[t_ref, e_ref]), + sym::Result, + ItemDef::newtype(context.ty.env, result_ty, &[t_ref, e_ref]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/core/url.rs b/libs/@local/hashql/core/src/module/std_lib/core/url.rs index 2cfb8ff0f8e..6d10f2a9bed 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/url.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/url.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -21,17 +25,22 @@ pub(in crate::module::std_lib) struct Url { impl<'heap> StandardLibraryModule<'heap> for Url { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreUrl; + fn name() -> Symbol<'heap> { sym::url } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // TODO: consider making this constructor private via intrinsic (requires VM) // newtype Url = String; - let url_ty = types::url(&lib.ty); - def.push(sym::Url, ItemDef::newtype(lib.ty.env, url_ty, &[])); + let url_ty = types::url(&context.ty); + def.push(sym::Url, ItemDef::newtype(context.ty.env, url_ty, &[])); def } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs b/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs index de7ae31ca65..1be4967497b 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -21,16 +25,21 @@ pub(in crate::module::std_lib) struct Uuid { impl<'heap> StandardLibraryModule<'heap> for Uuid { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreUuid; + fn name() -> Symbol<'heap> { sym::uuid } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // TODO: consider making this constructor private via intrinsic (requires VM) // newtype Uuid = String; - let uuid = ItemDef::newtype(lib.ty.env, types::uuid(&lib.ty), &[]); + let uuid = ItemDef::newtype(context.ty.env, types::uuid(&context.ty), &[]); def.push(sym::Uuid, uuid); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/body.rs b/libs/@local/hashql/core/src/module/std_lib/graph/body.rs index d2db4de2838..7978eabf5f9 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/body.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/body.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, - std_lib::{self, ModuleDef, StandardLibraryModule, core::func, decl}, + std_lib::{ + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::func, decl, + }, }, symbol::{Symbol, sym}, }; @@ -14,29 +18,33 @@ pub(in crate::module::std_lib) struct Body { impl<'heap> StandardLibraryModule<'heap> for Body { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphBody; + fn name() -> Symbol<'heap> { sym::body } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); - let heap = lib.heap; + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let graph = lib.manifest::(); + let graph = cache.request::(context); - let mut graph_param = graph.expect_type(heap.intern_symbol("Graph")); + let mut graph_param = graph.expect_type(sym::Graph); let mut graph_returns = graph_param; - graph_param.instantiate(&mut lib.instantiate); - graph_returns.instantiate(&mut lib.instantiate); + graph_param.instantiate(&mut context.instantiate); + graph_returns.instantiate(&mut context.instantiate); // `filter(graph: Graph, predicate: fn(vertex: T) -> bool) -> Graph;` // Once https://linear.app/hash/issue/H-4741/hashql-support-for-type-guards lands this will change to: // `filter(graph: Graph, predicate: fn(vertex: T) -> entity is U) -> Graph;` - let decl = decl!(lib; - (graph: lib.ty.apply([(graph_param.arguments[0].id, T)], graph_param.id), - predicate: lib.ty.closure([T], lib.ty.boolean()) - ) -> lib.ty.apply([(graph_returns.arguments[0].id, T)], graph_returns.id) + let decl = decl!(context; + (graph: context.ty.apply([(graph_param.arguments[0].id, T)], graph_param.id), + predicate: context.ty.closure([T], context.ty.boolean()) + ) -> context.ty.apply([(graph_returns.arguments[0].id, T)], graph_returns.id) ); func(&mut def, sym::path::graph_body_filter, [sym::filter], decl); diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs b/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs index 5567dae0173..0109d5c70b7 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs @@ -1,9 +1,10 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, std_lib::{ - self, ModuleDef, StandardLibraryModule, + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, core::{func, option::types::option}, decl, }, @@ -21,35 +22,39 @@ pub(in crate::module::std_lib) struct Entity { impl<'heap> StandardLibraryModule<'heap> for Entity { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphEntity; + fn name() -> Symbol<'heap> { sym::entity } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); - let heap = lib.heap; + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let mut entity_ty = lib - .manifest::() - .expect_newtype(heap.intern_symbol("Entity")); - entity_ty.instantiate(&mut lib.instantiate); + let mut entity_ty = cache + .request::(context) + .expect_newtype(sym::Entity); + entity_ty.instantiate(&mut context.instantiate); - let versioned_url_ty = lib - .manifest::() - .expect_newtype(heap.intern_symbol("VersionedUrl")); + let versioned_url_ty = cache + .request::(context) + .expect_newtype(sym::VersionedUrl); - let json_path_ty = lib - .manifest::() - .expect_type(heap.intern_symbol("JsonPath")); + let json_path_ty = cache + .request::(context) + .expect_type(sym::JsonPath); // `is_of_type(entity: Entity, depth: Integer, type: VersionedUrl) -> Boolean` // see: https://linear.app/hash/issue/H-4741/hashql-support-for-type-guards // see: https://linear.app/hash/issue/H-4742/hashql-allow-is-of-type-to-be-queried-using-an-entitytype - let decl = decl!(lib; - (entity: lib.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), - depth: lib.ty.integer(), + let decl = decl!(context; + (entity: context.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), + depth: context.ty.integer(), type: versioned_url_ty.id - ) -> lib.ty.boolean() + ) -> context.ty.boolean() ); func( @@ -60,10 +65,10 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { ); // `property(entity: Entity, path: JsonPath) -> Option` - let decl = decl!(lib; - (entity: lib.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), + let decl = decl!(context; + (entity: context.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), path: json_path_ty.id - ) -> option(&lib.ty, lib.ty.unknown()) + ) -> option(&context.ty, context.ty.unknown()) ); func( diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/head.rs b/libs/@local/hashql/core/src/module/std_lib/graph/head.rs index 5489040d803..c2085e0405a 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/head.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/head.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, - std_lib::{self, ModuleDef, StandardLibraryModule, core::func, decl}, + std_lib::{ + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::func, decl, + }, }, symbol::{Symbol, sym}, }; @@ -18,34 +22,39 @@ pub(in crate::module::std_lib) struct Head { impl<'heap> StandardLibraryModule<'heap> for Head { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphHead; + fn name() -> Symbol<'heap> { sym::head } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); - let heap = lib.heap; + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let query_temporal_axes_ty = lib - .manifest::() + let query_temporal_axes_ty = cache + .request::(context) .expect_type(sym::QueryTemporalAxes); - let mut graph_ty = lib - .manifest::() - .expect_type(heap.intern_symbol("Graph")); - graph_ty.instantiate(&mut lib.instantiate); + let mut graph_ty = cache + .request::(context) + .expect_type(sym::Graph); + graph_ty.instantiate(&mut context.instantiate); - let mut entity = lib - .manifest::() - .expect_newtype(heap.intern_symbol("Entity")); - entity.instantiate(&mut lib.instantiate); + let mut entity = cache + .request::(context) + .expect_newtype(sym::Entity); + entity.instantiate(&mut context.instantiate); // ::graph::head::entities(axis: TimeAxis) -> Graph>; - let entities_returns = lib.ty.apply( + let entities_returns = context.ty.apply( [( graph_ty.arguments[0].id, - lib.ty - .apply([(entity.arguments[0].id, lib.ty.unknown())], entity.id), + context + .ty + .apply([(entity.arguments[0].id, context.ty.unknown())], entity.id), )], graph_ty.id, ); @@ -53,7 +62,7 @@ impl<'heap> StandardLibraryModule<'heap> for Head { &mut def, sym::path::graph_head_entities, [sym::entities], - decl!(lib; <>(axis: query_temporal_axes_ty.id) -> entities_returns), + decl!(context; <>(axis: query_temporal_axes_ty.id) -> entities_returns), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs index ea6bc9ae423..98cbc327ee6 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs @@ -1,3 +1,12 @@ +use core::alloc::Allocator; + +use crate::{ + module::std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, + symbol::{Symbol, sym}, +}; + pub(in crate::module::std_lib) mod body; pub(in crate::module::std_lib) mod entity; pub(in crate::module::std_lib) mod head; @@ -6,14 +15,6 @@ pub mod temporal; pub(in crate::module::std_lib) mod tmp; pub mod types; -use crate::{ - module::{ - StandardLibrary, - std_lib::{ItemDef, ModuleDef, StandardLibraryModule}, - }, - symbol::{Symbol, sym}, -}; - pub(in crate::module::std_lib) struct Graph { _dependencies: (), } @@ -29,12 +30,17 @@ impl<'heap> StandardLibraryModule<'heap> for Graph { self::types::Types, ); + const CACHE_ID: CacheId = CacheId::Graph; + fn name() -> Symbol<'heap> { sym::graph } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype Graph = ('marker: T) // @@ -42,20 +48,22 @@ impl<'heap> StandardLibraryModule<'heap> for Graph { // internally to track the graph's type information. The field is named `'marker` using an // identifier that cannot be referenced in user code (`'` is not a valid symbol in any // identifier). - let t_arg = lib.ty.fresh_argument("T"); - let t_ref = lib.ty.hydrate_argument(t_arg); - let t_param = lib.ty.param(t_arg); + let t_arg = context.ty.fresh_argument(sym::T); + let t_ref = context.ty.hydrate_argument(t_arg); + let t_param = context.ty.param(t_arg); - let graph_ty = lib.ty.generic( + let graph_ty = context.ty.generic( [t_arg], - lib.ty - .opaque("::graph::Graph", lib.ty.r#struct([("'marker", t_param)])), + context.ty.opaque( + sym::path::graph::Graph, + context.ty.r#struct([(sym::internal::marker, t_param)]), + ), ); def.push( - lib.heap.intern_symbol("Graph"), + sym::Graph, // Export as `type` rather than `newtype` since Graph is not intended to be // user-constructible - ItemDef::r#type(lib.ty.env, graph_ty, &[t_ref]), + ItemDef::r#type(context.ty.env, graph_ty, &[t_ref]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs b/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs index daf05eb5d9d..ef4fcb7f39c 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, - std_lib::{self, ModuleDef, StandardLibraryModule, core::func, decl}, + std_lib::{ + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::func, decl, + }, }, symbol::{Symbol, sym}, }; @@ -14,22 +18,26 @@ pub(in crate::module::std_lib) struct Tail { impl<'heap> StandardLibraryModule<'heap> for Tail { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTail; + fn name() -> Symbol<'heap> { sym::tail } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); - let heap = lib.heap; + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let graph = lib.manifest::(); + let graph = cache.request::(context); - let mut graph_ty = graph.expect_type(heap.intern_symbol("Graph")); - graph_ty.instantiate(&mut lib.instantiate); + let mut graph_ty = graph.expect_type(sym::Graph); + graph_ty.instantiate(&mut context.instantiate); // `collect(graph: Graph) -> List;` - let decl = decl!(lib; - (graph: lib.ty.apply([(graph_ty.arguments[0].id, T)], graph_ty.id)) -> lib.ty.list(T) + let decl = decl!(context; + (graph: context.ty.apply([(graph_ty.arguments[0].id, T)], graph_ty.id)) -> context.ty.list(T) ); func( diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs b/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs index c5c1eb07f94..d98c9730a92 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -108,175 +112,189 @@ pub(in crate::module::std_lib) struct Temporal { impl<'heap> StandardLibraryModule<'heap> for Temporal { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTemporal; + fn name() -> Symbol<'heap> { sym::temporal } #[expect(clippy::too_many_lines, clippy::similar_names)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype Timestamp = Integer // // TODO: replace with a dedicated primitive type in the future. - let timestamp_ty = types::timestamp(&lib.ty); + let timestamp_ty = types::timestamp(&context.ty); def.push( sym::Timestamp, - ItemDef::newtype(lib.ty.env, timestamp_ty, &[]), + ItemDef::newtype(context.ty.env, timestamp_ty, &[]), ); // newtype DecisionTime = T - let dt_t_arg = lib.ty.fresh_argument(sym::T); - let dt_t_ref = lib.ty.hydrate_argument(dt_t_arg); - let dt_t_param = lib.ty.param(dt_t_arg); + let dt_t_arg = context.ty.fresh_argument(sym::T); + let dt_t_ref = context.ty.hydrate_argument(dt_t_arg); + let dt_t_param = context.ty.param(dt_t_arg); - let decision_time_ty = lib.ty.generic( + let decision_time_ty = context.ty.generic( [(dt_t_arg, None)], - types::decision_time(&lib.ty, dt_t_param), + types::decision_time(&context.ty, dt_t_param), ); def.push( sym::DecisionTime, - ItemDef::newtype(lib.ty.env, decision_time_ty, &[dt_t_ref]), + ItemDef::newtype(context.ty.env, decision_time_ty, &[dt_t_ref]), ); // newtype TransactionTime = T - let tt_t_arg = lib.ty.fresh_argument(sym::T); - let tt_t_ref = lib.ty.hydrate_argument(tt_t_arg); - let tt_t_param = lib.ty.param(tt_t_arg); + let tt_t_arg = context.ty.fresh_argument(sym::T); + let tt_t_ref = context.ty.hydrate_argument(tt_t_arg); + let tt_t_param = context.ty.param(tt_t_arg); - let transaction_time_ty = lib.ty.generic( + let transaction_time_ty = context.ty.generic( [(tt_t_arg, None)], - types::transaction_time(&lib.ty, tt_t_param), + types::transaction_time(&context.ty, tt_t_param), ); def.push( sym::TransactionTime, - ItemDef::newtype(lib.ty.env, transaction_time_ty, &[tt_t_ref]), + ItemDef::newtype(context.ty.env, transaction_time_ty, &[tt_t_ref]), ); // newtype UnboundedTemporalBound = Null - let unbounded_bound_ty = types::unbounded_temporal_bound(&lib.ty); + let unbounded_bound_ty = types::unbounded_temporal_bound(&context.ty); def.push( sym::UnboundedTemporalBound, - ItemDef::newtype(lib.ty.env, unbounded_bound_ty, &[]), + ItemDef::newtype(context.ty.env, unbounded_bound_ty, &[]), ); // newtype InclusiveTemporalBound = Timestamp - let inclusive_bound_ty = types::inclusive_temporal_bound(&lib.ty); + let inclusive_bound_ty = types::inclusive_temporal_bound(&context.ty); def.push( sym::InclusiveTemporalBound, - ItemDef::newtype(lib.ty.env, inclusive_bound_ty, &[]), + ItemDef::newtype(context.ty.env, inclusive_bound_ty, &[]), ); // newtype ExclusiveTemporalBound = Timestamp - let exclusive_bound_ty = types::exclusive_temporal_bound(&lib.ty); + let exclusive_bound_ty = types::exclusive_temporal_bound(&context.ty); def.push( sym::ExclusiveTemporalBound, - ItemDef::newtype(lib.ty.env, exclusive_bound_ty, &[]), + ItemDef::newtype(context.ty.env, exclusive_bound_ty, &[]), ); // type TemporalBound = UnboundedTemporalBound | InclusiveTemporalBound // | ExclusiveTemporalBound let temporal_bound_ty = - lib.ty + context + .ty .union([unbounded_bound_ty, inclusive_bound_ty, exclusive_bound_ty]); def.push( sym::TemporalBound, - ItemDef::r#type(lib.ty.env, temporal_bound_ty, &[]), + ItemDef::r#type(context.ty.env, temporal_bound_ty, &[]), ); // type FiniteTemporalBound = InclusiveTemporalBound | ExclusiveTemporalBound - let finite_bound_ty = lib.ty.union([inclusive_bound_ty, exclusive_bound_ty]); + let finite_bound_ty = context.ty.union([inclusive_bound_ty, exclusive_bound_ty]); def.push( sym::FiniteTemporalBound, - ItemDef::r#type(lib.ty.env, finite_bound_ty, &[]), + ItemDef::r#type(context.ty.env, finite_bound_ty, &[]), ); // type OpenTemporalBound = ExclusiveTemporalBound | UnboundedTemporalBound - let open_bound_ty = lib.ty.union([exclusive_bound_ty, unbounded_bound_ty]); + let open_bound_ty = context.ty.union([exclusive_bound_ty, unbounded_bound_ty]); def.push( sym::OpenTemporalBound, - ItemDef::r#type(lib.ty.env, open_bound_ty, &[]), + ItemDef::r#type(context.ty.env, open_bound_ty, &[]), ); // newtype Interval = (start: S, end: E) - let interval_s_arg = lib.ty.fresh_argument(sym::S); - let interval_s_ref = lib.ty.hydrate_argument(interval_s_arg); - let interval_s_param = lib.ty.param(interval_s_arg); + let interval_s_arg = context.ty.fresh_argument(sym::S); + let interval_s_ref = context.ty.hydrate_argument(interval_s_arg); + let interval_s_param = context.ty.param(interval_s_arg); - let interval_e_arg = lib.ty.fresh_argument(sym::E); - let interval_e_ref = lib.ty.hydrate_argument(interval_e_arg); - let interval_e_param = lib.ty.param(interval_e_arg); + let interval_e_arg = context.ty.fresh_argument(sym::E); + let interval_e_ref = context.ty.hydrate_argument(interval_e_arg); + let interval_e_param = context.ty.param(interval_e_arg); - let interval_ty = lib.ty.generic( + let interval_ty = context.ty.generic( [(interval_s_arg, None), (interval_e_arg, None)], - types::interval(&lib.ty, interval_s_param, interval_e_param), + types::interval(&context.ty, interval_s_param, interval_e_param), ); def.push( sym::Interval, - ItemDef::newtype(lib.ty.env, interval_ty, &[interval_s_ref, interval_e_ref]), + ItemDef::newtype( + context.ty.env, + interval_ty, + &[interval_s_ref, interval_e_ref], + ), ); // type LeftClosedTemporalInterval = // Interval - let left_closed_interval_ty = types::interval(&lib.ty, inclusive_bound_ty, open_bound_ty); + let left_closed_interval_ty = + types::interval(&context.ty, inclusive_bound_ty, open_bound_ty); def.push( sym::LeftClosedTemporalInterval, - ItemDef::r#type(lib.ty.env, left_closed_interval_ty, &[]), + ItemDef::r#type(context.ty.env, left_closed_interval_ty, &[]), ); // type RightBoundedTemporalInterval = // Interval let right_bounded_interval_ty = - types::interval(&lib.ty, temporal_bound_ty, finite_bound_ty); + types::interval(&context.ty, temporal_bound_ty, finite_bound_ty); def.push( sym::RightBoundedTemporalInterval, - ItemDef::r#type(lib.ty.env, right_bounded_interval_ty, &[]), + ItemDef::r#type(context.ty.env, right_bounded_interval_ty, &[]), ); // newtype PinnedTransactionTimeTemporalAxes = ( // pinned: TransactionTime, // variable: DecisionTime, // ) - let pinned_tx_ty = lib.ty.opaque( + let pinned_tx_ty = context.ty.opaque( sym::path::PinnedTransactionTimeTemporalAxes, - lib.ty.r#struct([ - (sym::pinned, types::transaction_time(&lib.ty, timestamp_ty)), + context.ty.r#struct([ + ( + sym::pinned, + types::transaction_time(&context.ty, timestamp_ty), + ), ( sym::variable, - types::decision_time(&lib.ty, right_bounded_interval_ty), + types::decision_time(&context.ty, right_bounded_interval_ty), ), ]), ); def.push( sym::PinnedTransactionTimeTemporalAxes, - ItemDef::newtype(lib.ty.env, pinned_tx_ty, &[]), + ItemDef::newtype(context.ty.env, pinned_tx_ty, &[]), ); // newtype PinnedDecisionTimeTemporalAxes = ( // pinned: DecisionTime, // variable: TransactionTime, // ) - let pinned_dt_ty = lib.ty.opaque( + let pinned_dt_ty = context.ty.opaque( sym::path::PinnedDecisionTimeTemporalAxes, - lib.ty.r#struct([ - (sym::pinned, types::decision_time(&lib.ty, timestamp_ty)), + context.ty.r#struct([ + (sym::pinned, types::decision_time(&context.ty, timestamp_ty)), ( sym::variable, - types::transaction_time(&lib.ty, right_bounded_interval_ty), + types::transaction_time(&context.ty, right_bounded_interval_ty), ), ]), ); def.push( sym::PinnedDecisionTimeTemporalAxes, - ItemDef::newtype(lib.ty.env, pinned_dt_ty, &[]), + ItemDef::newtype(context.ty.env, pinned_dt_ty, &[]), ); // type QueryTemporalAxes = PinnedTransactionTimeTemporalAxes // | PinnedDecisionTimeTemporalAxes - let query_temporal_axes_ty = lib.ty.union([pinned_tx_ty, pinned_dt_ty]); + let query_temporal_axes_ty = context.ty.union([pinned_tx_ty, pinned_dt_ty]); def.push( sym::QueryTemporalAxes, - ItemDef::r#type(lib.ty.env, query_temporal_axes_ty, &[]), + ItemDef::r#type(context.ty.env, query_temporal_axes_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs b/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs index e85867fb5b0..3d2fca7edf8 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs @@ -1,8 +1,13 @@ +use core::alloc::Allocator; + use crate::{ + intern::Interned, module::{ - StandardLibrary, locals::TypeDef, - std_lib::{self, ModuleDef, StandardLibraryModule, core::func}, + std_lib::{ + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::func, + }, }, symbol::{Symbol, sym}, r#type::TypeId, @@ -15,15 +20,20 @@ pub(in crate::module::std_lib) struct Tmp { impl<'heap> StandardLibraryModule<'heap> for Tmp { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTmp; + fn name() -> Symbol<'heap> { sym::tmp } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let query_temporal_axes_ty = lib - .manifest::() + let query_temporal_axes_ty = cache + .request::(context) .expect_type(sym::QueryTemporalAxes); // ::graph::tmp::decision_time_now() -> TimeAxis @@ -32,8 +42,10 @@ impl<'heap> StandardLibraryModule<'heap> for Tmp { sym::path::graph::tmp::decision_time_now, [sym::decision_time_now], TypeDef { - id: lib.ty.closure([] as [TypeId; 0], query_temporal_axes_ty.id), - arguments: lib.ty.env.intern_generic_argument_references(&[]), + id: context + .ty + .closure([] as [TypeId; 0], query_temporal_axes_ty.id), + arguments: Interned::empty(), }, ); diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs index c89d11c7ee5..35b8eb2fe15 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule}, + module::std_lib::{ + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -372,32 +374,37 @@ pub(in crate::module::std_lib) struct Entity { impl<'heap> StandardLibraryModule<'heap> for Entity { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTypesKnowledgeEntity; + fn name() -> Symbol<'heap> { sym::entity } #[expect(clippy::too_many_lines)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); - - let uuid_ty = lib - .manifest::() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); + + let uuid_ty = cache + .request::(context) .expect_newtype(sym::Uuid) .id; - let web_id_ty = lib - .manifest::() + let web_id_ty = cache + .request::(context) .expect_newtype(sym::WebId) .id; - let versioned_url_ty = lib - .manifest::() + let versioned_url_ty = cache + .request::(context) .expect_newtype(sym::VersionedUrl) .id; - let left_closed_interval_ty = lib - .manifest::() + let left_closed_interval_ty = cache + .request::(context) .expect_type(sym::LeftClosedTemporalInterval) .id; - let ty = &lib.ty; + let ty = &context.ty; let entity_uuid_ty = types::entity_uuid(ty, Some(types::EntityUuidDependencies { uuid: uuid_ty })); @@ -523,13 +530,13 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { ); // Entity - let t_arg = lib.ty.fresh_argument(sym::T); - let t_ref = lib.ty.hydrate_argument(t_arg); - let t_param = lib.ty.param(t_arg); - let entity_ty = lib.ty.generic( + let t_arg = context.ty.fresh_argument(sym::T); + let t_ref = context.ty.hydrate_argument(t_arg); + let t_param = context.ty.param(t_arg); + let entity_ty = context.ty.generic( [t_arg], types::entity( - &lib.ty, + &context.ty, t_param, Some(types::EntityDependencies { link_data: link_data_ty, @@ -540,7 +547,7 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { ); def.push( sym::Entity, - ItemDef::newtype(lib.ty.env, entity_ty, &[t_ref]), + ItemDef::newtype(context.ty.env, entity_ty, &[t_ref]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs index 39359faff79..b3994b6221d 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs @@ -1,9 +1,10 @@ +use core::alloc::Allocator; + pub mod entity; use crate::{ - module::{ - StandardLibrary, - std_lib::{ModuleDef, StandardLibraryModule}, + module::std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -13,11 +14,16 @@ pub(in crate::module::std_lib) struct Knowledge; impl<'heap> StandardLibraryModule<'heap> for Knowledge { type Children = (self::entity::Entity,); + const CACHE_ID: CacheId = CacheId::GraphTypesKnowledge; + fn name() -> Symbol<'heap> { sym::knowledge } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs index 3c6ae5eeabd..65c28b35a51 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs @@ -1,8 +1,9 @@ +use core::alloc::Allocator; + // This is in a separate module, to facilitate: https://linear.app/hash/issue/H-4735/hashql-convert-rust-types-into-hashql-types use crate::{ - module::{ - StandardLibrary, - std_lib::{ModuleDef, StandardLibraryModule}, + module::std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -22,11 +23,16 @@ impl<'heap> StandardLibraryModule<'heap> for Types { self::principal::Principal, ); + const CACHE_ID: CacheId = CacheId::GraphTypes; + fn name() -> Symbol<'heap> { sym::types } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs index d7187cd2ebc..0f6cccd9d31 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule, core::option::types::option}, + module::std_lib::{ + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, core::option::types::option, }, symbol::{Symbol, sym}, }; @@ -16,41 +18,47 @@ pub(in crate::module::std_lib) struct EntityType { impl<'heap> StandardLibraryModule<'heap> for EntityType { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTypesOntologyEntityType; + fn name() -> Symbol<'heap> { sym::entity_type } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); - let heap = lib.heap; + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype EntityTypeMetadata = (web_id: Option) - let web_id = lib - .manifest::() - .expect_newtype(heap.intern_symbol("WebId")); - let entity_type_metadata_ty = lib.ty.opaque( - "::graph::types::ontology::entity_type::EntityTypeMetadata", - lib.ty.r#struct([("web_id", option(&lib.ty, web_id.id))]), + let web_id = cache + .request::(context) + .expect_newtype(sym::WebId); + let entity_type_metadata_ty = context.ty.opaque( + sym::path::graph::types::ontology::entity_type::EntityTypeMetadata, + context + .ty + .r#struct([(sym::web_id, option(&context.ty, web_id.id))]), ); def.push( - heap.intern_symbol("EntityTypeMetadata"), - ItemDef::newtype(lib.ty.env, entity_type_metadata_ty, &[]), + sym::EntityTypeMetadata, + ItemDef::newtype(context.ty.env, entity_type_metadata_ty, &[]), ); // newtype EntityType = (id: VersionedUrl, metadata: EntityTypeMetadata) - let versioned_url = lib - .manifest::() - .expect_newtype(heap.intern_symbol("VersionedUrl")); - let entity_id_ty = lib.ty.opaque( - "::graph::types::ontology::entity_type::EntityType", - lib.ty.r#struct([ - ("id", versioned_url.id), - ("metadata", entity_type_metadata_ty), + let versioned_url = cache + .request::(context) + .expect_newtype(sym::VersionedUrl); + let entity_id_ty = context.ty.opaque( + sym::path::graph::types::ontology::entity_type::EntityType, + context.ty.r#struct([ + (sym::id, versioned_url.id), + (sym::metadata, entity_type_metadata_ty), ]), ); def.push( - heap.intern_symbol("EntityType"), - ItemDef::newtype(lib.ty.env, entity_id_ty, &[]), + sym::EntityType, + ItemDef::newtype(context.ty.env, entity_id_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs index ed9b49b76a6..1e17850802c 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule}, + module::std_lib::{ + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -65,34 +67,44 @@ pub(in crate::module::std_lib) struct Ontology { impl<'heap> StandardLibraryModule<'heap> for Ontology { type Children = (self::entity_type::EntityType,); + const CACHE_ID: CacheId = CacheId::GraphTypesOntology; + fn name() -> Symbol<'heap> { sym::ontology } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype OntologyTypeVersion = String; - let ontology_type_version_ty = types::ontology_type_version(&lib.ty); + let ontology_type_version_ty = types::ontology_type_version(&context.ty); def.push( sym::OntologyTypeVersion, - ItemDef::newtype(lib.ty.env, ontology_type_version_ty, &[]), + ItemDef::newtype(context.ty.env, ontology_type_version_ty, &[]), ); - let url_ty = lib - .manifest::() + let url_ty = cache + .request::(context) .expect_newtype(sym::Url) .id; // TODO: consider making this constructor private via intrinsic (requires VM) // newtype BaseUrl = Url; - let base_url_ty = - types::base_url(&lib.ty, Some(types::BaseUrlDependencies { url: url_ty })); - def.push(sym::BaseUrl, ItemDef::newtype(lib.ty.env, base_url_ty, &[])); + let base_url_ty = types::base_url( + &context.ty, + Some(types::BaseUrlDependencies { url: url_ty }), + ); + def.push( + sym::BaseUrl, + ItemDef::newtype(context.ty.env, base_url_ty, &[]), + ); // newtype VersionedUrl = (base_url: BaseUrl, version: OntologyTypeVersion); let versioned_url_ty = types::versioned_url( - &lib.ty, + &context.ty, Some(types::VersionedUrlDependencies { base_url: base_url_ty, ontology_type_version: ontology_type_version_ty, @@ -100,7 +112,7 @@ impl<'heap> StandardLibraryModule<'heap> for Ontology { ); def.push( sym::VersionedUrl, - ItemDef::newtype(lib.ty.env, versioned_url_ty, &[]), + ItemDef::newtype(context.ty.env, versioned_url_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs index fdbbe962a42..e03fb858cad 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule}, + module::std_lib::{ + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -40,28 +42,33 @@ pub(in crate::module::std_lib) struct ActorGroup { impl<'heap> StandardLibraryModule<'heap> for ActorGroup { type Children = (self::web::Web,); + const CACHE_ID: CacheId = CacheId::GraphTypesPrincipalActorGroup; + fn name() -> Symbol<'heap> { sym::actor_group } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype ActorGroupEntityUuid = EntityUuid; // (we just set it to Uuid to avoid any cycles) // see: https://linear.app/hash/issue/H-4616/hashql-use-expression-hoisting // see: https://linear.app/hash/issue/H-4735/hashql-convert-rust-types-into-hashql-types - let uuid_ty = lib - .manifest::() + let uuid_ty = cache + .request::(context) .expect_newtype(sym::Uuid) .id; let actor_group_entity_uuid_ty = types::actor_group_entity_uuid( - &lib.ty, + &context.ty, Some(types::ActorGroupEntityUuidDependencies { uuid: uuid_ty }), ); def.push( sym::ActorGroupEntityUuid, - ItemDef::newtype(lib.ty.env, actor_group_entity_uuid_ty, &[]), + ItemDef::newtype(context.ty.env, actor_group_entity_uuid_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs index 18d196e246e..bd1096f5a14 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule}, + module::std_lib::{ + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -39,25 +41,30 @@ pub(in crate::module::std_lib) struct Web { impl<'heap> StandardLibraryModule<'heap> for Web { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTypesPrincipalActorGroupWeb; + fn name() -> Symbol<'heap> { sym::web } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype WebId = ActorGroupEntityUuid; - let actor_group_entity_uuid_ty = lib - .manifest::() + let actor_group_entity_uuid_ty = cache + .request::(context) .expect_newtype(sym::ActorGroupEntityUuid) .id; let web_id_ty = types::web_id( - &lib.ty, + &context.ty, Some(types::WebIdDependencies { actor_group_entity_uuid: actor_group_entity_uuid_ty, }), ); - def.push(sym::WebId, ItemDef::newtype(lib.ty.env, web_id_ty, &[])); + def.push(sym::WebId, ItemDef::newtype(context.ty.env, web_id_ty, &[])); def } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs index 82dbda1c525..02af6e5c0cc 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs @@ -1,9 +1,10 @@ +use core::alloc::Allocator; + pub mod actor_group; use crate::{ - module::{ - StandardLibrary, - std_lib::{ModuleDef, StandardLibraryModule}, + module::std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -15,11 +16,16 @@ pub(in crate::module::std_lib) struct Principal { impl<'heap> StandardLibraryModule<'heap> for Principal { type Children = (self::actor_group::ActorGroup,); + const CACHE_ID: CacheId = CacheId::GraphTypesPrincipal; + fn name() -> Symbol<'heap> { sym::principal } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs index e7a23ecf350..a39ab7c6483 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + pub(in crate::module::std_lib) mod special_form; pub(in crate::module::std_lib) mod r#type; -use super::{ModuleDef, StandardLibrary, StandardLibraryModule}; +use super::{CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}; use crate::symbol::{Symbol, sym}; pub(in crate::module::std_lib) struct Kernel; @@ -9,11 +11,16 @@ pub(in crate::module::std_lib) struct Kernel; impl<'heap> StandardLibraryModule<'heap> for Kernel { type Children = (self::special_form::SpecialForm, self::r#type::Type); + const CACHE_ID: CacheId = CacheId::Kernel; + fn name() -> Symbol<'heap> { sym::kernel } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs index b7f981f273b..adff9ab1d39 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs @@ -1,8 +1,13 @@ +use core::alloc::Allocator; + use crate::{ + intern::Interned, module::{ item::IntrinsicValueItem, locals::TypeDef, - std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, }, symbol::{Symbol, sym}, }; @@ -10,9 +15,9 @@ use crate::{ pub(in crate::module::std_lib) struct SpecialForm; impl SpecialForm { - fn make<'heap>( - lib: &StandardLibrary<'_, 'heap>, - def: &mut ModuleDef<'heap>, + fn make<'heap, S: Allocator>( + context: &StandardLibraryContext<'_, 'heap, S>, + def: &mut ModuleDef<'heap, S>, path: Symbol<'heap>, names: impl IntoIterator>, @@ -20,8 +25,8 @@ impl SpecialForm { let value = IntrinsicValueItem { name: path, r#type: TypeDef { - id: lib.ty.never(), - arguments: lib.ty.env.intern_generic_argument_references(&[]), + id: context.ty.never(), + arguments: Interned::empty(), }, }; @@ -32,29 +37,34 @@ impl SpecialForm { impl<'heap> StandardLibraryModule<'heap> for SpecialForm { type Children = (); + const CACHE_ID: CacheId = CacheId::KernelSpecialForm; + fn name() -> Symbol<'heap> { sym::special_form } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - Self::make(lib, &mut def, sym::path::r#if, [sym::r#if]); - Self::make(lib, &mut def, sym::path::r#as, [sym::r#as]); - Self::make(lib, &mut def, sym::path::r#let, [sym::r#let]); - Self::make(lib, &mut def, sym::path::r#type, [sym::r#type]); - Self::make(lib, &mut def, sym::path::newtype, [sym::newtype]); - Self::make(lib, &mut def, sym::path::r#use, [sym::r#use]); - Self::make(lib, &mut def, sym::path::r#fn, [sym::r#fn]); - Self::make(lib, &mut def, sym::path::input, [sym::input]); + Self::make(context, &mut def, sym::path::r#if, [sym::r#if]); + Self::make(context, &mut def, sym::path::r#as, [sym::r#as]); + Self::make(context, &mut def, sym::path::r#let, [sym::r#let]); + Self::make(context, &mut def, sym::path::r#type, [sym::r#type]); + Self::make(context, &mut def, sym::path::newtype, [sym::newtype]); + Self::make(context, &mut def, sym::path::r#use, [sym::r#use]); + Self::make(context, &mut def, sym::path::r#fn, [sym::r#fn]); + Self::make(context, &mut def, sym::path::input, [sym::input]); Self::make( - lib, + context, &mut def, sym::path::access, [sym::access, sym::symbol::dot], ); Self::make( - lib, + context, &mut def, sym::path::index, [sym::index, sym::symbol::brackets], diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs index c7ffaf4cb3f..5de86d96115 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs @@ -1,7 +1,11 @@ +use core::alloc::Allocator; + use crate::{ module::{ item::IntrinsicTypeItem, - std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, }, symbol::{Symbol, sym}, r#type::TypeId, @@ -10,18 +14,18 @@ use crate::{ pub(in crate::module::std_lib) struct Type; impl Type { - fn primitive<'heap>( - lib: &StandardLibrary<'_, 'heap>, - def: &mut ModuleDef<'heap>, + fn primitive<'heap, S: Allocator>( + context: &StandardLibraryContext<'_, 'heap, S>, + def: &mut ModuleDef<'heap, S>, name: Symbol<'heap>, id: TypeId, ) -> usize { - let item = ItemDef::r#type(lib.ty.env, id, &[]); + let item = ItemDef::r#type(context.ty.env, id, &[]); def.push(name, item) } - fn intrinsic<'heap>( - def: &mut ModuleDef<'heap>, + fn intrinsic<'heap, S: Allocator>( + def: &mut ModuleDef<'heap, S>, path: Symbol<'heap>, ident: Symbol<'heap>, ) -> usize { @@ -34,24 +38,29 @@ impl Type { impl<'heap> StandardLibraryModule<'heap> for Type { type Children = (); + const CACHE_ID: CacheId = CacheId::KernelType; + fn name() -> Symbol<'heap> { sym::r#type } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - Self::primitive(lib, &mut def, sym::Boolean, lib.ty.boolean()); - Self::primitive(lib, &mut def, sym::Null, lib.ty.null()); - Self::primitive(lib, &mut def, sym::Number, lib.ty.number()); - Self::primitive(lib, &mut def, sym::Integer, lib.ty.integer()); + Self::primitive(context, &mut def, sym::Boolean, context.ty.boolean()); + Self::primitive(context, &mut def, sym::Null, context.ty.null()); + Self::primitive(context, &mut def, sym::Number, context.ty.number()); + Self::primitive(context, &mut def, sym::Integer, context.ty.integer()); // Natural does not yet exist, due to lack of support for refinements - Self::primitive(lib, &mut def, sym::String, lib.ty.string()); + Self::primitive(context, &mut def, sym::String, context.ty.string()); - let unknown = Self::primitive(lib, &mut def, sym::Unknown, lib.ty.unknown()); + let unknown = Self::primitive(context, &mut def, sym::Unknown, context.ty.unknown()); def.alias(unknown, sym::symbol::question_mark); - let never = Self::primitive(lib, &mut def, sym::Never, lib.ty.never()); + let never = Self::primitive(context, &mut def, sym::Never, context.ty.never()); def.alias(never, sym::symbol::exclamation); // Struct/Tuple are purposefully excluded, as they are diff --git a/libs/@local/hashql/core/src/module/std_lib/mod.rs b/libs/@local/hashql/core/src/module/std_lib/mod.rs index d6631f3213c..f1c3f4198ec 100644 --- a/libs/@local/hashql/core/src/module/std_lib/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/mod.rs @@ -2,16 +2,13 @@ pub mod core; pub mod graph; mod kernel; -use ::core::{iter, num::NonZero}; +use ::core::{alloc::Allocator, iter, mem, mem::MaybeUninit, num::NonZero}; -use super::{ModuleId, ModuleRegistry, item::IntrinsicItem, locals::TypeDef}; +use super::{Module, ModuleId, PartialModuleRegistry, item::IntrinsicItem, locals::TypeDef}; use crate::{ - collections::SmallVec, - heap::Heap, - module::{ - PartialModule, - item::{ConstructorItem, Item, ItemKind}, - }, + heap::BumpAllocator as _, + id::{Id as _, bit_vec::FiniteBitSet}, + module::item::{ConstructorItem, Item, ItemKind}, symbol::Symbol, r#type::{ TypeBuilder, TypeId, @@ -74,17 +71,27 @@ impl<'heap> ModuleEntry<'heap> { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -struct ModuleDef<'heap>(SmallVec>); +struct ModuleDef<'heap, S: Allocator> { + entries: Vec, S>, + /// The number of items that will be emitted when this module is built. + /// + /// This differs from `entries.len()` because `Newtype` entries emit two items (a constructor + /// and a type), while other entries emit one. + emitted_len: usize, +} -impl<'heap> ModuleDef<'heap> { - const fn new() -> Self { - Self(SmallVec::new()) +impl<'heap, S: Allocator> ModuleDef<'heap, S> { + fn new_in(alloc: S) -> Self { + Self { + entries: Vec::with_capacity_in(16, alloc), + emitted_len: 0, + } } fn push(&mut self, name: Symbol<'heap>, def: ItemDef<'heap>) -> usize { - let index = self.0.len(); - self.0.push(ModuleEntry::new(name, def)); + let index = self.entries.len(); + self.emitted_len += def.emitted_len(); + self.entries.push(ModuleEntry::new(name, def)); index } @@ -96,7 +103,7 @@ impl<'heap> ModuleDef<'heap> { ) { let names = names.into_iter(); - self.0.reserve(names.size_hint().0); + self.entries.reserve(names.size_hint().0); for name in names { self.push(name, def); @@ -104,15 +111,16 @@ impl<'heap> ModuleDef<'heap> { } fn alias(&mut self, index: usize, alias: Symbol<'heap>) -> usize { - let item = self.0[index].alias(alias); + let item = self.entries[index].alias(alias); + self.emitted_len += item.def.emitted_len(); - let index = self.0.len(); - self.0.push(item); + let index = self.entries.len(); + self.entries.push(item); index } fn find(&self, name: Symbol<'heap>) -> Option> { - self.0.iter().find(|item| item.name == name).copied() + self.entries.iter().find(|item| item.name == name).copied() } #[track_caller] @@ -137,138 +145,281 @@ impl<'heap> ModuleDef<'heap> { } } -pub(super) struct StandardLibrary<'env, 'heap> { - heap: &'heap Heap, - instantiate: InstantiateEnvironment<'env, 'heap>, - registry: &'env ModuleRegistry<'heap>, - ty: TypeBuilder<'env, 'heap>, - modules: SmallVec<(::core::any::TypeId, ModuleDef<'heap>)>, +impl ItemDef<'_> { + /// Returns the number of items emitted when building this definition. + /// + /// `Newtype` emits both a constructor and a type; other variants emit one item. + const fn emitted_len(self) -> usize { + match self { + Self::Newtype(_) => 2, + Self::Type(_) | Self::Intrinsic(_) => 1, + } + } } -impl<'env, 'heap> StandardLibrary<'env, 'heap> { - pub(super) fn new( - environment: &'env Environment<'heap>, - registry: &'env ModuleRegistry<'heap>, - ) -> Self { +/// Index into the `ModuleCache` entries array. +/// +/// Variants are listed in preorder traversal of the module tree (parent before children, left +/// before right). Adding a new stdlib module requires adding a variant here in the correct +/// position. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, hashql_macros::Id)] +#[id(const, crate = crate)] +enum CacheId { + Core, + CoreBits, + CoreBool, + CoreCmp, + CoreJson, + CoreMath, + CoreOption, + CoreResult, + CoreUrl, + CoreUuid, + Kernel, + KernelSpecialForm, + KernelType, + Graph, + GraphTemporal, + GraphHead, + GraphBody, + GraphTail, + GraphEntity, + GraphTmp, + GraphTypes, + GraphTypesKnowledge, + GraphTypesKnowledgeEntity, + GraphTypesOntology, + GraphTypesOntologyEntityType, + GraphTypesPrincipal, + GraphTypesPrincipalActorGroup, + GraphTypesPrincipalActorGroupWeb, +} + +struct StandardLibraryContext<'env, 'heap, S: Allocator> { + pub instantiate: InstantiateEnvironment<'env, 'heap>, + pub registry: &'env mut PartialModuleRegistry<'heap, S>, + pub ty: TypeBuilder<'env, 'heap>, + pub alloc: S, +} + +struct ModuleCache<'heap, S: Allocator> { + entries: Box<[MaybeUninit>], S>, + /// Tracks which cache entries have been initialized. + /// + /// Bit `i` is set when `entries[i]` has been written. The stdlib has fewer than 64 modules, so + /// a `u64`-backed `FiniteBitSet` suffices. + occupied: FiniteBitSet, +} + +#[expect(unsafe_code)] +impl<'heap, S: Allocator> ModuleCache<'heap, S> { + fn new_in(count: u32, alloc: S) -> Self { Self { - heap: environment.heap, - instantiate: InstantiateEnvironment::new(environment), - registry, - ty: TypeBuilder::synthetic(environment), - modules: SmallVec::new(), + entries: Box::new_uninit_slice_in(count as usize, alloc), + occupied: FiniteBitSet::new_empty(count), } } - fn define_cached(&mut self) -> usize + fn request>( + &mut self, + context: &mut StandardLibraryContext<'_, 'heap, S>, + ) -> &ModuleDef<'heap, S> where - M: StandardLibraryModule<'heap>, + S: Clone, { - let module_id = ::core::any::TypeId::of::(); - if let Some(position) = self.modules.iter().position(|&(id, _)| id == module_id) { - position - } else { - let contents = M::define(self); - - let position = self.modules.len(); - self.modules.push((module_id, contents)); + let id = T::CACHE_ID; - position + if self.occupied.contains(id) { + // SAFETY: the bit is set only after the entry has been initialized. + return unsafe { self.entries[id.as_usize()].assume_init_ref() }; } + + let module = T::define(context, self); + let value = self.entries[id.as_usize()].write(module); + self.occupied.insert(id); + value } - fn manifest(&mut self) -> &ModuleDef<'heap> + fn build( + &mut self, + context: &mut StandardLibraryContext<'_, 'heap, S>, + depth: NonZero, + parent: ModuleId, + ) -> ModuleId where - M: StandardLibraryModule<'heap>, + T: StandardLibraryModule<'heap>, + S: Clone, { - let index = self.define_cached::(); + let id = context.registry.provision_module(); - &self.modules[index].1 - } + let def = self.request::(context); - fn build(&mut self, depth: NonZero, parent: ModuleId) -> ModuleId - where - M: StandardLibraryModule<'heap>, - { - self.registry.intern_module(|id| { - let items = &self.manifest::().0; - - let mut output = SmallVec::with_capacity(items.capacity() + M::Children::LENGTH); - - for &ModuleEntry { name, def: kind } in items { - let items = match kind { - ItemDef::Intrinsic(intrinsic) => [Some(ItemKind::Intrinsic(intrinsic)), None], - ItemDef::Type(def) => [Some(ItemKind::Type(def)), None], - ItemDef::Newtype(def) => [ - Some(ItemKind::Constructor(ConstructorItem { r#type: def })), - Some(ItemKind::Type(def)), - ], - }; - - for kind in items.into_iter().flatten() { - output.push(Item { - module: id.value(), + let heap = context.registry.heap; + let output = heap.allocate_slice_uninit(def.emitted_len + T::Children::LENGTH); + + let mut cursor = 0; + for &ModuleEntry { name, def } in &def.entries { + match def { + ItemDef::Intrinsic(intrinsic) => { + output[cursor].write(Item { + module: id, name, - kind, + kind: ItemKind::Intrinsic(intrinsic), }); + cursor += 1; } - } + ItemDef::Type(typedef) => { + output[cursor].write(Item { + module: id, + name, + kind: ItemKind::Type(typedef), + }); + cursor += 1; + } + ItemDef::Newtype(typedef) => { + output[cursor].write(Item { + module: id, + name, + kind: ItemKind::Constructor(ConstructorItem { r#type: typedef }), + }); + cursor += 1; - // create all the child modules - let children_names = M::Children::names(); - let children_modules = M::Children::modules(self, depth.saturating_add(1), id.value()); + output[cursor].write(Item { + module: id, + name, + kind: ItemKind::Type(typedef), + }); + cursor += 1; + } + } + } - for (name, module) in children_names.into_iter().zip(children_modules) { - output.push(Item { - module: id.value(), + let remaining = &mut output[cursor..]; + let (_, remaining) = remaining.write_iter( + T::Children::names() + .into_iter() + .zip(T::Children::modules( + context, + self, + depth.saturating_add(1), + id, + )) + .map(|(name, child)| Item { + module: id, name, - kind: ItemKind::Module(module), - }); - } + kind: ItemKind::Module(child), + }), + ); + + assert!(remaining.is_empty()); + + let module = Module { + id, + name: T::name(), + parent, + depth, + // SAFETY: assertion ensures that the output buffer is fully initialized before reading. + items: unsafe { output.assume_init_ref() }, + }; + + context.registry.insert_module(module); + module.id + } +} - PartialModule { - name: M::name(), - parent, - depth, - items: self.registry.intern_items(&output), +#[expect(unsafe_code)] +impl Drop for ModuleCache<'_, S> { + fn drop(&mut self) { + for index in &self.occupied { + // SAFETY: the bit is set only after the entry has been initialized. + unsafe { + self.entries[index.as_usize()].assume_init_drop(); } - }) + } } +} + +pub(super) struct StandardLibrary<'env, 'heap, S: Allocator> { + context: StandardLibraryContext<'env, 'heap, S>, +} - pub(super) fn register(&mut self) { +impl<'env, 'heap, S: Allocator> StandardLibrary<'env, 'heap, S> { + pub(super) fn new( + environment: &'env Environment<'heap>, + registry: &'env mut PartialModuleRegistry<'heap, S>, + alloc: S, + ) -> Self { + Self { + context: StandardLibraryContext { + registry, + instantiate: InstantiateEnvironment::new(environment), + ty: TypeBuilder::synthetic(environment), + alloc, + }, + } + } + + #[expect( + clippy::cast_possible_truncation, + reason = "module count is less than u32::MAX" + )] + pub(super) fn register(&mut self) + where + S: Clone, + { type Root = (self::core::Core, self::kernel::Kernel, self::graph::Graph); const ONE: NonZero = NonZero::new(1).unwrap(); + const MODULE_COUNT: usize = mem::variant_count::(); + + debug_assert_eq!( + Root::ITEMS, + MODULE_COUNT, + "CacheId variant count does not match module tree size", + ); + + let alloc = self.context.alloc.clone(); + + let mut cache = ModuleCache::new_in(MODULE_COUNT as u32, alloc); - let roots: smallvec::SmallVec<_, 3> = Root::modules(self, ONE, ModuleId::ROOT) + let mut output = [ModuleId::ROOT; Root::LENGTH]; + for (module, output) in Root::modules(&mut self.context, &mut cache, ONE, ModuleId::ROOT) .into_iter() - .collect(); + .zip(&mut output) + { + *output = module; + } - for id in roots { - self.registry.register(id); + for module in output { + self.context.registry.register(module); } } } trait Submodules<'heap> { const LENGTH: usize; + const ITEMS: usize; fn names() -> impl IntoIterator>; - fn modules( - lib: &mut StandardLibrary<'_, 'heap>, + fn modules( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, depth: NonZero, parent: ModuleId, ) -> impl IntoIterator; } impl<'heap> Submodules<'heap> for () { + const ITEMS: usize = 0; const LENGTH: usize = 0; fn names() -> impl IntoIterator> { iter::empty() } - fn modules( - _: &mut StandardLibrary<'_, 'heap>, + fn modules( + _: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, _: NonZero, _: ModuleId, ) -> impl IntoIterator { @@ -295,6 +446,7 @@ macro_rules! impl_submodules { $($item: StandardLibraryModule<'heap>,)* { const LENGTH: usize = ${count($item)}; + const ITEMS: usize = ${count($item)} $(+ <$item::Children as Submodules<'heap>>::ITEMS)*; fn names() -> impl IntoIterator> { $(let $item = $item::name();)* @@ -302,12 +454,13 @@ macro_rules! impl_submodules { [$($item),*] } - fn modules( - lib: &mut StandardLibrary<'_, 'heap>, + fn modules( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, depth: NonZero, parent: ModuleId, ) -> impl IntoIterator { - $(let $item = lib.build::<$item>(depth, parent);)* + $(let $item = cache.build::<$item>(context, depth, parent);)* [$($item),*] } @@ -318,11 +471,20 @@ macro_rules! impl_submodules { impl_submodules!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); trait StandardLibraryModule<'heap>: 'static { + /// Unique index for this module in the `ModuleCache` entries array. + /// + /// Assigned in preorder traversal of the module tree (parent before children, left before + /// right). IDs must be unique across all stdlib modules and less than 64. + const CACHE_ID: CacheId; + type Children: Submodules<'heap>; fn name() -> Symbol<'heap>; - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap>; + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S>; } /// Declares a generic function type with parameters and return type. @@ -358,7 +520,11 @@ macro_rules! decl { TypeDef { id: closure, - arguments: $lib.ty.env.intern_generic_argument_references(&[$(${concat($generic, _ref)}),*]), + arguments: if ${count($generic)} > 0 { + $lib.ty.env.intern_generic_argument_references(&[$(${concat($generic, _ref)}),*]) + } else { + crate::intern::Interned::empty() + }, } }}; } diff --git a/libs/@local/hashql/core/src/symbol/sym.rs b/libs/@local/hashql/core/src/symbol/sym.rs index 965711af38f..8fd7272a427 100644 --- a/libs/@local/hashql/core/src/symbol/sym.rs +++ b/libs/@local/hashql/core/src/symbol/sym.rs @@ -50,6 +50,8 @@ hashql_macros::define_symbols! { end, entity, Entity, + EntityType, + EntityTypeMetadata, entity_edition_id, entity_id, entity_type_ids, @@ -189,6 +191,7 @@ hashql_macros::define_symbols! { body, tmp, decision_time_now, + Graph, graph, types, principal, @@ -200,10 +203,13 @@ hashql_macros::define_symbols! { url, result, json, + JsonPath, + JsonPathSegment, // [tidy] sort alphabetically end internal: { - ClosureEnv: "'" + ClosureEnv: "'", + marker: "'marker" }, symbol: { @@ -351,15 +357,28 @@ hashql_macros::define_symbols! { sqrt: "::core::math::sqrt", cbrt: "::core::math::cbrt", root: "::core::math::root", + }, + result: { + Ok: "::core::result::Ok", + Err: "::core::result::Err", } }, graph: { + Graph: "::graph::Graph", entity: { is_of_type: "::graph::entity::is_of_type", property: "::graph::entity::property", }, tmp: { decision_time_now: "::graph::tmp::decision_time_now", + }, + types: { + ontology: { + entity_type: { + EntityType: "::graph::types::ontology::entity_type::EntityType", + EntityTypeMetadata: "::graph::types::ontology::entity_type::EntityTypeMetadata", + } + } } } // [tidy] sort alphabetically end diff --git a/libs/darwin-kperf/events/src/lib.rs b/libs/darwin-kperf/events/src/lib.rs index f8b368ee82d..376d24e782a 100644 --- a/libs/darwin-kperf/events/src/lib.rs +++ b/libs/darwin-kperf/events/src/lib.rs @@ -59,7 +59,7 @@ impl Cpu { b"a15" => Some(Self::M2), b"a16" | b"as1" | b"as2" | b"as3" => Some(Self::M3), b"as4" | b"as4-1" | b"as4-2" => Some(Self::M4), - b"as5" | b"as5-1" | b"as5-2" => Some(Self::M5), + b"as5" | b"as5-2" => Some(Self::M5), _ => None, } }