diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index aa8723317..716733d79 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -13,8 +13,8 @@ use crate::compile::MetaInfo; use crate::compile::{ComponentRef, ContextError, IntoComponent, Item, ItemBuf, Names}; use crate::hash; use crate::module::{ - Fields, InternalEnum, Module, ModuleAssociated, ModuleAttributeMacro, ModuleConstant, - ModuleFunction, ModuleMacro, ModuleType, TypeSpecification, + Fields, InternalEnum, Module, ModuleAssociated, ModuleAssociatedKind, ModuleAttributeMacro, + ModuleConstant, ModuleFunction, ModuleMacro, ModuleType, TypeSpecification, }; use crate::runtime::{ AttributeMacroHandler, ConstValue, FunctionHandler, MacroHandler, Protocol, RuntimeContext, @@ -741,23 +741,6 @@ impl Context { .hash(assoc.container.hash) .with_function_parameters(assoc.name.function_parameters); - let signature = meta::Signature { - #[cfg(feature = "doc")] - is_async: assoc.is_async, - #[cfg(feature = "doc")] - deprecated: assoc.deprecated.try_clone()?, - #[cfg(feature = "doc")] - args: assoc.args, - #[cfg(feature = "doc")] - return_type: assoc.return_type.as_ref().map(|f| f.hash), - #[cfg(feature = "doc")] - argument_types: assoc - .argument_types - .iter() - .map(|f| f.as_ref().map(|f| f.hash)) - .try_collect()?, - }; - // If the associated function is a named instance function - register it // under the name of the item it corresponds to unless it's a field // function. @@ -771,35 +754,69 @@ impl Context { .with_type_parameters(info.type_parameters) .with_function_parameters(assoc.name.function_parameters); - self.constants.try_insert( - Hash::associated_function(hash, Protocol::INTO_TYPE_NAME), - ConstValue::String(item.try_to_string()?), - )?; - - self.insert_native_fn(hash, &assoc.handler)?; - Some(item) + Some((hash, item)) } else { None }; - self.insert_native_fn(hash, &assoc.handler)?; + let kind = match &assoc.kind { + ModuleAssociatedKind::Constant(c) => { + if let Some((hash, ..)) = &item { + self.constants.try_insert(*hash, c.value.try_clone()?)?; + } + + self.constants.try_insert(hash, c.value.try_clone()?)?; + meta::Kind::Const + } + ModuleAssociatedKind::Function(f) => { + let signature = meta::Signature { + #[cfg(feature = "doc")] + is_async: f.is_async, + #[cfg(feature = "doc")] + deprecated: assoc.deprecated.try_clone()?, + #[cfg(feature = "doc")] + args: f.args, + #[cfg(feature = "doc")] + return_type: f.return_type.as_ref().map(|f| f.hash), + #[cfg(feature = "doc")] + argument_types: f + .argument_types + .iter() + .map(|f| f.as_ref().map(|f| f.hash)) + .try_collect()?, + }; + + if let Some((hash, item)) = &item { + self.constants.try_insert( + Hash::associated_function(*hash, Protocol::INTO_TYPE_NAME), + ConstValue::String(item.try_to_string()?), + )?; + + self.insert_native_fn(*hash, &f.handler)?; + } + + self.insert_native_fn(hash, &f.handler)?; + + meta::Kind::Function { + associated: Some(assoc.name.kind.try_clone()?), + signature, + is_test: false, + is_bench: false, + parameters: Hash::EMPTY + .with_type_parameters(info.type_parameters) + .with_function_parameters(assoc.name.function_parameters), + #[cfg(feature = "doc")] + container: Some(assoc.container.hash), + #[cfg(feature = "doc")] + parameter_types: assoc.name.parameter_types.try_clone()?, + } + } + }; self.install_meta(ContextMeta { hash, - item, - kind: meta::Kind::Function { - associated: Some(assoc.name.kind.try_clone()?), - signature, - is_test: false, - is_bench: false, - parameters: Hash::EMPTY - .with_type_parameters(info.type_parameters) - .with_function_parameters(assoc.name.function_parameters), - #[cfg(feature = "doc")] - container: Some(assoc.container.hash), - #[cfg(feature = "doc")] - parameter_types: assoc.name.parameter_types.try_clone()?, - }, + item: item.map(|(_, item)| item), + kind, #[cfg(feature = "doc")] docs: assoc.docs.try_clone()?, })?; diff --git a/crates/rune/src/module.rs b/crates/rune/src/module.rs index 54c0cd91c..34dbec99a 100644 --- a/crates/rune/src/module.rs +++ b/crates/rune/src/module.rs @@ -194,6 +194,7 @@ pub(crate) struct AssociatedKey { #[derive(TryClone)] pub(crate) struct ModuleFunction { pub(crate) item: ItemBuf, + pub(crate) docs: Docs, pub(crate) handler: Arc, #[cfg(feature = "doc")] pub(crate) is_async: bool, @@ -205,26 +206,41 @@ pub(crate) struct ModuleFunction { pub(crate) return_type: Option, #[cfg(feature = "doc")] pub(crate) argument_types: Box<[Option]>, - pub(crate) docs: Docs, } #[derive(TryClone)] -pub(crate) struct ModuleAssociated { - pub(crate) container: FullTypeOf, - pub(crate) container_type_info: TypeInfo, - pub(crate) name: AssociatedName, +pub(crate) struct ModuleAssociatedConstant { + pub(crate) value: ConstValue, +} + +#[derive(TryClone)] +pub(crate) struct ModuleAssociatedFunction { pub(crate) handler: Arc, #[cfg(feature = "doc")] pub(crate) is_async: bool, #[cfg(feature = "doc")] - pub(crate) deprecated: Option>, - #[cfg(feature = "doc")] pub(crate) args: Option, #[cfg(feature = "doc")] pub(crate) return_type: Option, #[cfg(feature = "doc")] pub(crate) argument_types: Box<[Option]>, +} + +#[derive(TryClone)] +pub(crate) enum ModuleAssociatedKind { + Constant(ModuleAssociatedConstant), + Function(ModuleAssociatedFunction), +} + +#[derive(TryClone)] +pub(crate) struct ModuleAssociated { + pub(crate) container: FullTypeOf, + pub(crate) container_type_info: TypeInfo, + pub(crate) name: AssociatedName, pub(crate) docs: Docs, + #[cfg(feature = "doc")] + pub(crate) deprecated: Option>, + pub(crate) kind: ModuleAssociatedKind, } /// Handle to a macro inserted into a module. diff --git a/crates/rune/src/module/function_meta.rs b/crates/rune/src/module/function_meta.rs index c505a7c11..0bda74d6c 100644 --- a/crates/rune/src/module/function_meta.rs +++ b/crates/rune/src/module/function_meta.rs @@ -254,6 +254,15 @@ impl Associated { container_type_info: T::type_info(), }) } + + /// Get unique key for the associated item. + pub(crate) fn as_key(&self) -> alloc::Result { + Ok(AssociatedKey { + type_hash: self.container.hash, + kind: self.name.kind.try_clone()?, + parameters: self.name.function_parameters, + }) + } } /// Runtime data for an associated function. @@ -337,15 +346,6 @@ impl AssociatedFunctionData { argument_types: A::into_box()?, }) } - - /// Get associated key. - pub(crate) fn assoc_key(&self) -> alloc::Result { - Ok(AssociatedKey { - type_hash: self.associated.container.hash, - kind: self.associated.name.kind.try_clone()?, - parameters: self.associated.name.function_parameters, - }) - } } /// The kind of a [`FunctionMeta`]. diff --git a/crates/rune/src/module/module.rs b/crates/rune/src/module/module.rs index 7c5af28ba..512847c77 100644 --- a/crates/rune/src/module/module.rs +++ b/crates/rune/src/module/module.rs @@ -14,9 +14,9 @@ use crate::module::function_meta::{ }; use crate::module::{ AssociatedKey, Async, EnumMut, Function, FunctionKind, InstallWith, InstanceFunction, - InternalEnum, InternalEnumMut, ItemFnMut, ItemMut, ModuleAssociated, ModuleAttributeMacro, - ModuleConstant, ModuleFunction, ModuleMacro, ModuleType, Plain, TypeMut, TypeSpecification, - VariantMut, + InternalEnum, InternalEnumMut, ItemFnMut, ItemMut, ModuleAssociated, ModuleAssociatedConstant, + ModuleAssociatedFunction, ModuleAssociatedKind, ModuleAttributeMacro, ModuleConstant, + ModuleFunction, ModuleMacro, ModuleType, Plain, TypeMut, TypeSpecification, VariantMut, }; use crate::runtime::{ AttributeMacroHandler, ConstValue, FromValue, FullTypeOf, FunctionHandler, GeneratorState, @@ -54,9 +54,9 @@ where /// ``` /// use rune::{Any, Module}; /// - /// let mut m = Module::with_item(["module"]); m.function("floob", || - /// ()).build()?; - /// # Ok::<_, rune::ContextError>(()) + /// let mut m = Module::with_item(["module"])?; + /// m.function("floob", || ()).build()?; + /// # Ok::<_, rune::support::Error>(()) /// ``` #[inline] pub fn build(self) -> Result, ContextError> @@ -74,17 +74,23 @@ where /// /// # Errors /// - /// The function call will error if the specified type is not already - /// registered in the module. + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install /// /// ``` - /// use rune::{Any, Module}; + /// use rune::{Any, Context, Module}; /// /// #[derive(Any)] /// struct Thing; /// /// let mut m = Module::default(); - /// assert!(m.function("floob", || ()).build_associated::().is_err()); + /// m.function("floob", || ()).build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) /// ``` /// /// # Examples @@ -98,7 +104,7 @@ where /// let mut m = Module::default(); /// m.ty::()?; /// m.function("floob", || ()).build_associated::()?; - /// # Ok::<_, rune::ContextError>(()) + /// # Ok::<_, rune::support::Error>(()) /// ``` #[inline] pub fn build_associated(self) -> Result, ContextError> @@ -167,9 +173,9 @@ impl<'a, N> ModuleRawFunctionBuilder<'a, N> { /// use rune::{Any, Module}; /// use rune::runtime::VmResult; /// - /// let mut m = Module::with_item(["module"]); + /// let mut m = Module::with_item(["module"])?; /// m.raw_function("floob", |stac, args| VmResult::Ok(())).build()?; - /// # Ok::<_, rune::ContextError>(()) + /// # Ok::<_, rune::support::Error>(()) /// ``` #[inline] pub fn build(self) -> Result, ContextError> @@ -191,17 +197,23 @@ impl<'a, N> ModuleRawFunctionBuilder<'a, N> { /// /// # Errors /// - /// The function call will error if the specified type is not already - /// registered in the module. + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install /// /// ``` - /// use rune::{Any, Module}; + /// use rune::{Any, Module, Context}; /// /// #[derive(Any)] /// struct Thing; /// /// let mut m = Module::default(); - /// assert!(m.function("floob", || ()).build_associated::().is_err()); + /// m.function("floob", || ()).build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) /// ``` /// /// # Examples @@ -215,8 +227,8 @@ impl<'a, N> ModuleRawFunctionBuilder<'a, N> { /// /// let mut m = Module::default(); /// m.ty::()?; - /// m.raw_function("floob", || VmResult::Ok(())).build_associated::()?; - /// # Ok::<_, rune::ContextError>(()) + /// m.raw_function("floob", |_, _| VmResult::Ok(())).build_associated::()?; + /// # Ok::<_, rune::support::Error>(()) /// ``` #[inline] pub fn build_associated(self) -> Result, ContextError> @@ -289,33 +301,39 @@ where /// use rune::{Any, Module}; /// use rune::runtime::VmResult; /// - /// let mut m = Module::with_item(["module"]); + /// let mut m = Module::with_item(["module"])?; /// m.constant("NAME", "Hello World").build()?; - /// # Ok::<_, rune::ContextError>(()) + /// # Ok::<_, rune::support::Error>(()) /// ``` pub fn build(self) -> Result, ContextError> where N: IntoComponent, { let item = ItemBuf::with_item([self.name])?; - self.module.build_constant_inner(item, self.value) + self.module.insert_constant(item, self.value) } /// Build a constant that is associated with the static type `T`. /// /// # Errors /// - /// The function call will error if the specified type is not already - /// registered in the module. + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install /// /// ``` - /// use rune::{Any, Module}; + /// use rune::{Any, Context, Module}; /// /// #[derive(Any)] /// struct Thing; /// /// let mut m = Module::default(); - /// assert!(m.constant("CONSTANT", "Hello World").build_associated::().is_err()); + /// m.constant("CONSTANT", "Hello World").build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) /// ``` /// /// # Examples @@ -333,10 +351,13 @@ where /// ``` pub fn build_associated(self) -> Result, ContextError> where + T: TypeOf, N: ToInstance, { - let _ = self.name.to_instance()?; - todo!("implement associated constants") + let name = self.name.to_instance()?; + let associated = Associated::from_type::(name)?; + self.module + .insert_associated_constant(associated, self.value) } } @@ -821,16 +842,10 @@ impl Module { } } - fn build_constant_inner( - &mut self, - item: ItemBuf, - value: V, - ) -> Result, ContextError> + fn insert_constant(&mut self, item: ItemBuf, value: V) -> Result, ContextError> where V: ToValue, { - let hash = Hash::type_hash(&item); - let value = match value.to_value() { VmResult::Ok(v) => v, VmResult::Err(error) => return Err(ContextError::ValueError { error }), @@ -841,6 +856,8 @@ impl Module { VmResult::Err(error) => return Err(ContextError::ValueError { error }), }; + let hash = Hash::type_hash(&item); + if !self.names.try_insert(Name::Item(hash))? { return Err(ContextError::ConflictingConstantName { item, hash }); } @@ -855,6 +872,43 @@ impl Module { Ok(ItemMut { docs: &mut c.docs }) } + fn insert_associated_constant( + &mut self, + associated: Associated, + value: V, + ) -> Result, ContextError> + where + V: ToValue, + { + let value = match value.to_value() { + VmResult::Ok(v) => v, + VmResult::Err(error) => return Err(ContextError::ValueError { error }), + }; + + let value = match ::from_value(value) { + VmResult::Ok(v) => v, + VmResult::Err(error) => return Err(ContextError::ValueError { error }), + }; + + self.insert_associated_name(&associated)?; + + self.associated.try_push(ModuleAssociated { + container: associated.container, + container_type_info: associated.container_type_info, + name: associated.name, + #[cfg(feature = "doc")] + deprecated: None, + docs: Docs::EMPTY, + kind: ModuleAssociatedKind::Constant(ModuleAssociatedConstant { value }), + })?; + + let last = self.associated.last_mut().unwrap(); + + Ok(ItemMut { + docs: &mut last.docs, + }) + } + /// Register a native macro handler through its meta. /// /// The metadata must be provided by annotating the function with @@ -1147,7 +1201,7 @@ impl Module { let mut docs = Docs::EMPTY; docs.set_docs(meta.docs)?; docs.set_arguments(meta.arguments)?; - self.assoc_fn(data, docs) + self.insert_associated_function(data, docs) } } } @@ -1158,7 +1212,9 @@ impl Module { ) -> Result, ContextError> { match kind { FunctionMetaKind::Function(data) => self.function_inner(data, Docs::EMPTY), - FunctionMetaKind::AssociatedFunction(data) => self.assoc_fn(data, Docs::EMPTY), + FunctionMetaKind::AssociatedFunction(data) => { + self.insert_associated_function(data, Docs::EMPTY) + } } } @@ -1439,7 +1495,7 @@ impl Module { A: FunctionArgs, K: FunctionKind, { - self.assoc_fn( + self.insert_associated_function( AssociatedFunctionData::from_instance_function(name.to_instance()?, f)?, Docs::EMPTY, ) @@ -1487,7 +1543,7 @@ impl Module { F::Return: MaybeTypeOf, A: FunctionArgs, { - self.assoc_fn( + self.insert_associated_function( AssociatedFunctionData::from_instance_function(name.to_field_function(protocol)?, f)?, Docs::EMPTY, ) @@ -1527,7 +1583,7 @@ impl Module { A: FunctionArgs, { let name = AssociatedName::index(protocol, index); - self.assoc_fn( + self.insert_associated_function( AssociatedFunctionData::from_instance_function(name, f)?, Docs::EMPTY, ) @@ -1577,7 +1633,7 @@ impl Module { /// let mut module = Module::default(); /// /// module.raw_function("sum", sum) - /// .build()?; + /// .build()? /// .docs([ /// "Sum all numbers provided to the function." /// ])?; @@ -1587,7 +1643,6 @@ impl Module { pub fn raw_function(&mut self, name: N, f: F) -> ModuleRawFunctionBuilder<'_, N> where F: 'static + Fn(&mut Stack, usize) -> VmResult<()> + Send + Sync, - N: IntoComponent, { ModuleRawFunctionBuilder { module: self, @@ -1654,74 +1709,92 @@ impl Module { } /// Install an associated function. - fn assoc_fn( + fn insert_associated_function( &mut self, data: AssociatedFunctionData, docs: Docs, ) -> Result, ContextError> { - if !self.names.try_insert(Name::Associated(data.assoc_key()?))? { - return Err(match data.associated.name.kind { - meta::AssociatedKind::Protocol(protocol) => { - ContextError::ConflictingProtocolFunction { - type_info: data.associated.container_type_info, - name: protocol.name.try_into()?, - } - } - meta::AssociatedKind::FieldFn(protocol, field) => { - ContextError::ConflictingFieldFunction { - type_info: data.associated.container_type_info, - name: protocol.name.try_into()?, - field: field.try_into()?, - } - } - meta::AssociatedKind::IndexFn(protocol, index) => { - ContextError::ConflictingIndexFunction { - type_info: data.associated.container_type_info, - name: protocol.name.try_into()?, - index, - } - } - meta::AssociatedKind::Instance(name) => ContextError::ConflictingInstanceFunction { - type_info: data.associated.container_type_info, - name: name.try_into()?, - }, - }); - } + self.insert_associated_name(&data.associated)?; self.associated.try_push(ModuleAssociated { container: data.associated.container, container_type_info: data.associated.container_type_info, name: data.associated.name, - handler: data.handler, - #[cfg(feature = "doc")] - is_async: data.is_async, #[cfg(feature = "doc")] deprecated: data.deprecated, - #[cfg(feature = "doc")] - args: data.args, - #[cfg(feature = "doc")] - return_type: data.return_type, - #[cfg(feature = "doc")] - argument_types: data.argument_types, docs, + kind: ModuleAssociatedKind::Function(ModuleAssociatedFunction { + handler: data.handler, + #[cfg(feature = "doc")] + is_async: data.is_async, + #[cfg(feature = "doc")] + #[cfg(feature = "doc")] + args: data.args, + #[cfg(feature = "doc")] + return_type: data.return_type, + #[cfg(feature = "doc")] + argument_types: data.argument_types, + }), })?; let last = self.associated.last_mut().unwrap(); + #[cfg(feature = "doc")] + let last_fn = match &mut last.kind { + ModuleAssociatedKind::Function(f) => f, + _ => unreachable!(), + }; + Ok(ItemFnMut { docs: &mut last.docs, #[cfg(feature = "doc")] - is_async: &mut last.is_async, + is_async: &mut last_fn.is_async, #[cfg(feature = "doc")] deprecated: &mut last.deprecated, #[cfg(feature = "doc")] - args: &mut last.args, + args: &mut last_fn.args, #[cfg(feature = "doc")] - return_type: &mut last.return_type, + return_type: &mut last_fn.return_type, #[cfg(feature = "doc")] - argument_types: &mut last.argument_types, + argument_types: &mut last_fn.argument_types, }) } + + fn insert_associated_name(&mut self, associated: &Associated) -> Result<(), ContextError> { + if !self + .names + .try_insert(Name::Associated(associated.as_key()?))? + { + return Err(match &associated.name.kind { + meta::AssociatedKind::Protocol(protocol) => { + ContextError::ConflictingProtocolFunction { + type_info: associated.container_type_info.try_clone()?, + name: protocol.name.try_into()?, + } + } + meta::AssociatedKind::FieldFn(protocol, field) => { + ContextError::ConflictingFieldFunction { + type_info: associated.container_type_info.try_clone()?, + name: protocol.name.try_into()?, + field: field.as_ref().try_into()?, + } + } + meta::AssociatedKind::IndexFn(protocol, index) => { + ContextError::ConflictingIndexFunction { + type_info: associated.container_type_info.try_clone()?, + name: protocol.name.try_into()?, + index: *index, + } + } + meta::AssociatedKind::Instance(name) => ContextError::ConflictingInstanceFunction { + type_info: associated.container_type_info.try_clone()?, + name: name.as_ref().try_into()?, + }, + }); + } + + Ok(()) + } } impl AsRef for Module {