From 02145d1f2e1ed5358902923aa0f6b2317d10cac8 Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 15 Dec 2025 23:35:35 +0000 Subject: [PATCH 01/58] feat: Start of expression concepts --- plans/TODO.md | 24 +- src/expressions/concepts/actual.rs | 116 +++ src/expressions/concepts/dyn_impls.rs | 57 ++ src/expressions/concepts/form.rs | 64 ++ src/expressions/concepts/forms/any_mut.rs | 23 + src/expressions/concepts/forms/any_ref.rs | 24 + src/expressions/concepts/forms/argument.rs | 1 + src/expressions/concepts/forms/assignee.rs | 17 + .../concepts/forms/copy_on_write.rs | 29 + src/expressions/concepts/forms/late_bound.rs | 49 ++ src/expressions/concepts/forms/mod.rs | 24 + src/expressions/concepts/forms/mutable.rs | 16 + src/expressions/concepts/forms/owned.rs | 25 + .../concepts/forms/referenceable.rs | 52 ++ src/expressions/concepts/forms/shared.rs | 16 + src/expressions/concepts/mod.rs | 16 + src/expressions/concepts/type_impls.rs | 172 +++++ src/expressions/concepts/type_traits.rs | 200 ++++++ src/expressions/mod.rs | 11 +- src/expressions/type_resolution/arguments.rs | 2 +- .../type_resolution/interface_macros.rs | 13 +- src/interpretation/bindings.rs | 6 +- src/interpretation/refs.rs | 80 ++- src/misc/mut_rc_ref_cell.rs | 47 +- src/sandbox/dyn_value.rs | 4 + src/sandbox/gat_value.rs | 662 ------------------ 26 files changed, 1044 insertions(+), 706 deletions(-) create mode 100644 src/expressions/concepts/actual.rs create mode 100644 src/expressions/concepts/dyn_impls.rs create mode 100644 src/expressions/concepts/form.rs create mode 100644 src/expressions/concepts/forms/any_mut.rs create mode 100644 src/expressions/concepts/forms/any_ref.rs create mode 100644 src/expressions/concepts/forms/argument.rs create mode 100644 src/expressions/concepts/forms/assignee.rs create mode 100644 src/expressions/concepts/forms/copy_on_write.rs create mode 100644 src/expressions/concepts/forms/late_bound.rs create mode 100644 src/expressions/concepts/forms/mod.rs create mode 100644 src/expressions/concepts/forms/mutable.rs create mode 100644 src/expressions/concepts/forms/owned.rs create mode 100644 src/expressions/concepts/forms/referenceable.rs create mode 100644 src/expressions/concepts/forms/shared.rs create mode 100644 src/expressions/concepts/mod.rs create mode 100644 src/expressions/concepts/type_impls.rs create mode 100644 src/expressions/concepts/type_traits.rs delete mode 100644 src/sandbox/gat_value.rs diff --git a/plans/TODO.md b/plans/TODO.md index 236c3b85..c620586c 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -188,9 +188,27 @@ First, read the @./2025-11-vision.md - [x] `token_tree()` - [x] `open('(')` and `close(')')` +## Better handling of value sub-references + +- [x] Migrate from `Owned` having a span to a `Spanned` +- [ ] Implement and roll-out GATs + - [x] Initial shell implementation in `concepts` folder + - [x] Improved error handling in the macro (e.g. required arg after optional; no matching strongly-typed signature) + - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): + - [ ] Owned + - [ ] Mutable, Owned => Mutable + - [ ] Shared, Owned => Shared + - [ ] Assignee, Mutable <=> Assignee + - [ ] CopyOnWrite, Shared => CopyOnWrite x2, Owned => CopyOnWrite + - [ ] LateBound, Tons of conversions into it + - [ ] Argument, and `ArgumentOwnership` driven conversions into it + - [ ] Complete value definitions + - [ ] Replace `Owned`, `Shared` etc as type references to `Actual<..>` + - [ ] Remove old definitions + ## Methods and closures -- [ ] Improved Shared/Mutable handling - See the `Better handling of value sub-references` section +- [ ] Improved Shared/Mutable handling - See the `Better handling of value sub-references` section. The key requirement is we need to allow a `Shared` to map back to a `Shared`. Moving the "sharedness" to the leaves permits this. * A function specifies the bindings of its variables * If we have `my_len = |x: &array| x.len()` and invoke it as `my_len(a.b)` then when I invoke it, I need to end up with the variable `x := &a.b` @@ -204,13 +222,13 @@ First, read the @./2025-11-vision.md enum VariableContent { Owned(Rc>), Shared(SharedSubRcRefCell), - Mutable(MutSubRcRefCell), + Mutable(MutableSubRcRefCell), } // After enum VariableContent { Owned(ValueReferencable), Shared(ValueRef<'static>), // 'static => only SharedSubRcRefCell, no actual refs - Mutable(ValueMut<'static>), // 'static => only MutSubRcRefCell, no refs + Mutable(ValueMut<'static>), // 'static => only MutableSubRcRefCell, no refs } ``` - [ ] Introduce basic function values diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs new file mode 100644 index 00000000..38851213 --- /dev/null +++ b/src/expressions/concepts/actual.rs @@ -0,0 +1,116 @@ +use super::*; + +pub(crate) struct Actual<'a, T: IsType, F: IsForm>(pub(crate) T::Content<'a, F>); + +impl<'a, T: IsType, F: IsForm> Actual<'a, T, F> { + #[inline] + pub(crate) fn of(content: impl IntoValueContent<'a, Type = T, Form = F>) -> Self { + Actual(content.into_content()) + } + + #[inline] + pub(crate) fn map_type(self) -> Actual<'a, S, F> + where + T: MapToType, + { + Actual(T::map_to_type(self.0)) + } + + #[inline] + pub(crate) fn map_type_maybe>(self) -> Option> { + Some(Actual(U::maybe_map_from_type(self.0)?)) + } + + #[inline] + pub(crate) fn map_with>( + self, + ) -> Result, M::ShortCircuit<'a>> + where + T: IsHierarchyType, + { + T::map_with::<'a, F, M>(self.0).map(|c| Actual(c)) + } +} + +impl<'a, T: IsType, F: IsForm> Spanned> { + #[inline] + pub(crate) fn resolve_as>( + self, + description: &str, + ) -> ExecutionResult> { + let Spanned(value, span_range) = self; + U::resolve(value, span_range, description) + } +} + +impl<'a, T: IsType + MapToType, F: IsForm> Actual<'a, T, F> { + pub(crate) fn into_value(self) -> Actual<'a, ValueType, F> { + self.map_type() + } +} + +impl<'a, T: IsType, F: IsForm> Deref for Actual<'a, T, F> { + type Target = T::Content<'a, F>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, T: IsType, F: IsForm> DerefMut for Actual<'a, T, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub(crate) trait ContentMapper { + type TOut<'a>; + + fn map_leaf<'a, L: IsValueLeaf>(leaf: F::Leaf<'a, L>) -> Self::TOut<'a>; +} + +pub(crate) trait IsValueContent<'a> { + type Type: IsType; + type Form: IsForm; +} + +pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { + fn into_content(self) -> ::Content<'a, Self::Form>; + + #[inline] + fn into_actual(self) -> Actual<'a, Self::Type, Self::Form> + where + Self: Sized, + { + Actual::of(self) + } +} + +pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { + fn from_content(content: ::Content<'a, Self::Form>) -> Self; + + #[inline] + fn from_actual(actual: Actual<'a, Self::Type, Self::Form>) -> Self + where + Self: Sized, + { + Self::from_content(actual.0) + } +} + +impl<'a, T: IsType, F: IsForm> IsValueContent<'a> for Actual<'a, T, F> { + type Type = T; + type Form = F; +} + +impl<'a, T: IsType, F: IsForm> FromValueContent<'a> for Actual<'a, T, F> { + fn from_content(content: ::Content<'a, F>) -> Self { + Actual(content) + } +} + +impl<'a, T: IsType, F: IsForm> IntoValueContent<'a> for Actual<'a, T, F> { + fn into_content(self) -> ::Content<'a, F> { + self.0 + } +} diff --git a/src/expressions/concepts/dyn_impls.rs b/src/expressions/concepts/dyn_impls.rs new file mode 100644 index 00000000..c812aa4c --- /dev/null +++ b/src/expressions/concepts/dyn_impls.rs @@ -0,0 +1,57 @@ +use super::*; + +// NOTE: These should be moved out soon + +pub(crate) struct IterableType; + +pub(crate) trait IsIterable: 'static { + fn into_iterator(self: Box) -> ExecutionResult; + fn len(&self, error_span_range: SpanRange) -> ExecutionResult; +} + +impl IsType for IterableType { + type Content<'a, F: IsForm> = F::DynLeaf<'a, dyn IsIterable>; + + fn articled_type_name() -> &'static str { + "an iterable (e.g. array, list, etc.)" + } +} + +impl IsDynType for IterableType { + type DynContent = dyn IsIterable; +} + +impl<'a> IsValueContent<'a> for dyn IsIterable { + type Type = IterableType; + type Form = BeOwned; +} + +impl IsDynLeaf for dyn IsIterable {} + +impl MaybeMapFromType for IterableType { + fn maybe_map_from_type<'a>( + content: ::Content<'a, F>, + ) -> Option> { + match T::map_with::<'a, F, DynMapper>(content) { + Ok(_) => panic!("DynMapper is expected to always short-circuit"), + Err(dyn_leaf) => dyn_leaf, + } + } +} + +pub(crate) struct DynMapper(std::marker::PhantomData); + +impl GeneralMapper for DynMapper { + type OutputForm = F; // Unused + type ShortCircuit<'a> = Option>; + + fn map_leaf<'a, L: IsValueLeaf, T: IsType>( + leaf: F::Leaf<'a, L>, + ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> +// where + // T: for<'l> IsType = F::Leaf<'l, L>>, + // T: for<'l> IsType = ::Leaf<'l, L>> + { + Err(F::leaf_to_dyn(leaf)) + } +} diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs new file mode 100644 index 00000000..30a6f169 --- /dev/null +++ b/src/expressions/concepts/form.rs @@ -0,0 +1,64 @@ +use super::*; + +/// A type representing a particular form of a value's ownership. +/// +/// Examples include: +/// - [BeOwned] representing [Owned] (floating) value +/// - [BeShared] representing [Shared] references +/// - [BeMutable] representing [Mutable] references +/// - [BeCopyOnWrite] representing [CopyOnWrite] values +pub(crate) trait IsForm: Sized { + /// The standard leaf for a hierachical type + type Leaf<'a, T: IsValueLeaf>; + /// The container for a dyn Trait based type. + /// The DynLeaf can be similar to the standard leaf, but must be + /// able to support an unsized D. + type DynLeaf<'a, D: 'static + ?Sized>; + const ARGUMENT_OWNERSHIP: ArgumentOwnership; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option>; +} + +pub(crate) trait MapFromArgument: IsForm { + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult>; +} + +pub(crate) trait MapIntoReturned: IsForm { + fn into_returned_value( + value: Actual<'static, ValueType, Self>, + ) -> ExecutionResult; +} + +// Clashes with other blanket impl it will replace! +// +// impl< +// X: FromValueContent<'static, TypeData = T, Ownership = F>, +// F: IsForm + MapFromArgument, +// T: MaybeMapFromType, +// > IsArgument for X { +// type ValueType = T; +// const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; +// fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { +// let ownership_mapped = F::from_argument_value(value)?; +// let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; +// Ok(X::from_actual(type_mapped)) +// } +// } + +// Clashes with other blanket impl it will replace! +// +// impl< +// X: IntoValueContent<'static, TypeData = T, Ownership = F>, +// F: IsForm + MapIntoReturned, +// T: MapToType, +// > IsReturnable for X { +// fn to_returned_value(self) -> ExecutionResult { +// let type_mapped = self.into_actual() +// .map_type::(); +// F::into_returned_value(type_mapped) +// } +// } diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs new file mode 100644 index 00000000..3bfd49a2 --- /dev/null +++ b/src/expressions/concepts/forms/any_mut.rs @@ -0,0 +1,23 @@ +use super::*; + +pub(crate) struct BeAnyMut; +impl IsForm for BeAnyMut { + type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyMut<'a, T>; + type DynLeaf<'a, T: 'static + ?Sized> = crate::internal_prelude::AnyMut<'a, T>; + + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> { + leaf.map_optional(T::map_mut) + } +} + +impl MapFromArgument for BeAnyMut { + fn from_argument_value( + _value: ArgumentValue, + ) -> ExecutionResult> { + todo!() + } +} diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs new file mode 100644 index 00000000..42bda9c1 --- /dev/null +++ b/src/expressions/concepts/forms/any_ref.rs @@ -0,0 +1,24 @@ +use super::*; + +type AnyRef<'a, T> = Actual<'a, T, BeAnyRef>; + +pub(crate) struct BeAnyRef; +impl IsForm for BeAnyRef { + type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyRef<'a, T>; + type DynLeaf<'a, T: 'static + ?Sized> = crate::internal_prelude::AnyRef<'a, T>; + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized>( + leaf: Self::Leaf<'a, T>, + ) -> Option> { + leaf.map_optional(T::map_ref) + } +} + +impl MapFromArgument for BeAnyRef { + fn from_argument_value( + _value: ArgumentValue, + ) -> ExecutionResult> { + todo!() + } +} diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs new file mode 100644 index 00000000..4563e55b --- /dev/null +++ b/src/expressions/concepts/forms/argument.rs @@ -0,0 +1 @@ +use super::*; diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs new file mode 100644 index 00000000..b2c296e9 --- /dev/null +++ b/src/expressions/concepts/forms/assignee.rs @@ -0,0 +1,17 @@ +use super::*; + +type Assignee = Actual<'static, T, BeAssignee>; + +pub(crate) struct BeAssignee; +impl IsForm for BeAssignee { + type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; + type DynLeaf<'a, T: 'static + ?Sized> = MutableSubRcRefCell; + const ARGUMENT_OWNERSHIP: ArgumentOwnership = + ArgumentOwnership::Assignee { auto_create: false }; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> { + leaf.map_optional(T::map_mut) + } +} diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs new file mode 100644 index 00000000..fa685651 --- /dev/null +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -0,0 +1,29 @@ +use super::*; + +type CopyOnWrite = Actual<'static, T, BeCopyOnWrite>; + +pub(crate) struct BeCopyOnWrite; +impl IsForm for BeCopyOnWrite { + type Leaf<'a, T: IsValueLeaf> = CopyOnWriteContent; + type DynLeaf<'a, T: 'static + ?Sized> = CopyOnWriteContent>; + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + _leaf: Self::Leaf<'a, T>, + ) -> Option> { + // TODO: Add back once we add a map to LateBoundContent + todo!() + } +} + +/// Typically O == T or more specifically, ::Owned +pub(crate) enum CopyOnWriteContent { + /// An owned value that can be used directly + Owned(Owned), + /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). + /// In this case, returning a Cow is just an optimization and we can always clone infallibly. + SharedWithInfallibleCloning(Shared), + /// For use when the CopyOnWrite value represents a pre-cloned read-only value. + /// A transparent clone may fail in this case at use time. + SharedWithTransparentCloning(Shared), +} diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs new file mode 100644 index 00000000..a88ba4d0 --- /dev/null +++ b/src/expressions/concepts/forms/late_bound.rs @@ -0,0 +1,49 @@ +use super::*; + +/// Universal value type that can resolve to any concrete ownership type. +/// +/// Sometimes, a value can be accessed, but we don't yet know *how* we need to access it. +/// In this case, we attempt to load it with the most powerful access we can have, and convert it later. +/// +/// ## Example of requirement +/// For example, if we have `x[a].method()`, we first need to resolve the type of `x[a]` to know +/// whether the method needs `x[a]` to be a shared reference, mutable reference or an owned value. +/// +/// So instead, we take the most powerful access we can have for `x[a]`, and convert it later. +type LateBound = Actual<'static, T, BeLateBound>; + +pub(crate) struct BeLateBound; +impl IsForm for BeLateBound { + type Leaf<'a, T: IsValueLeaf> = LateBoundContent; + type DynLeaf<'a, T: 'static + ?Sized> = LateBoundContent>; + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + _leaf: Self::Leaf<'a, T>, + ) -> Option> { + // TODO: Add back once we add a map to LateBoundContent + todo!() + } +} + +pub(crate) enum LateBoundContent { + /// An owned value that can be converted to any ownership type + Owned(LateBoundOwned), + /// A copy-on-write value that can be converted to an owned value + CopyOnWrite(CopyOnWriteContent), + /// A mutable reference + Mutable(MutableSubRcRefCell), + /// A shared reference where mutable access failed for a specific reason + Shared(LateBoundShared), +} + +pub(crate) struct LateBoundOwned { + pub(crate) owned: O, + pub(crate) is_from_last_use: bool, +} + +/// A shared value where mutable access failed for a specific reason +pub(crate) struct LateBoundShared { + pub(crate) shared: SharedSubRcRefCell, + pub(crate) reason_not_mutable: syn::Error, +} diff --git a/src/expressions/concepts/forms/mod.rs b/src/expressions/concepts/forms/mod.rs new file mode 100644 index 00000000..2889a214 --- /dev/null +++ b/src/expressions/concepts/forms/mod.rs @@ -0,0 +1,24 @@ +#![allow(unused)] // Whilst we're building it out +use super::*; + +mod any_mut; +mod any_ref; +mod argument; +mod assignee; +mod copy_on_write; +mod late_bound; +mod mutable; +mod owned; +mod referenceable; +mod shared; + +pub(crate) use any_mut::*; +pub(crate) use any_ref::*; +pub(crate) use argument::*; +pub(crate) use assignee::*; +pub(crate) use copy_on_write::*; +pub(crate) use late_bound::*; +pub(crate) use mutable::*; +pub(crate) use owned::*; +pub(crate) use referenceable::*; +pub(crate) use shared::*; diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs new file mode 100644 index 00000000..e7b4ae4a --- /dev/null +++ b/src/expressions/concepts/forms/mutable.rs @@ -0,0 +1,16 @@ +use super::*; + +type Mutable = Actual<'static, T, BeMutable>; + +pub(crate) struct BeMutable; +impl IsForm for BeMutable { + type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; + type DynLeaf<'a, T: 'static + ?Sized> = MutableSubRcRefCell; + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> { + leaf.map_optional(T::map_mut) + } +} diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs new file mode 100644 index 00000000..a8e6d1a1 --- /dev/null +++ b/src/expressions/concepts/forms/owned.rs @@ -0,0 +1,25 @@ +use super::*; + +type Owned = Actual<'static, T, BeOwned>; + +pub(crate) struct BeOwned; +impl IsForm for BeOwned { + type Leaf<'a, T: IsValueLeaf> = T; + type DynLeaf<'a, T: 'static + ?Sized> = Box; + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> { + T::map_boxed(Box::new(leaf)) + } +} + +impl MapFromArgument for BeOwned { + fn from_argument_value( + _value: ArgumentValue, + ) -> ExecutionResult> { + // value.expect_owned() + todo!() + } +} diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs new file mode 100644 index 00000000..9406e41c --- /dev/null +++ b/src/expressions/concepts/forms/referenceable.rs @@ -0,0 +1,52 @@ +use super::*; + +type Referenceable = Actual<'static, T, BeReferenceable>; + +// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. +pub(crate) struct BeReferenceable; +impl IsForm for BeReferenceable { + type Leaf<'a, T: IsValueLeaf> = Rc>; + type DynLeaf<'a, T: 'static + ?Sized> = Rc>; + + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + _leaf: Self::Leaf<'a, T>, + ) -> Option> { + // Can't map Rc> to Rc> directly + panic!("Casting to dyn is not supported for Referenceable form") + } +} + +impl MapFromArgument for BeReferenceable { + fn from_argument_value( + _value: ArgumentValue, + ) -> ExecutionResult> { + // Rc::new(RefCell::new(value.expect_owned())) + todo!() + } +} + +impl<'a, T: IsHierarchyType> Actual<'a, T, BeOwned> { + pub(crate) fn into_referencable(self) -> Actual<'a, T, BeReferenceable> { + let Ok(output) = self.map_with::(); + output + } +} + +pub(crate) struct OwnedToReferencableMapper; + +impl GeneralMapper for OwnedToReferencableMapper { + type OutputForm = BeReferenceable; + type ShortCircuit<'a> = std::convert::Infallible; + + fn map_leaf< + 'a, + L: IsValueLeaf, + T: for<'l> IsType = ::Leaf<'l, L>>, + >( + leaf: ::Leaf<'a, L>, + ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> { + Ok(Rc::new(RefCell::new(leaf))) + } +} diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs new file mode 100644 index 00000000..a34cd5da --- /dev/null +++ b/src/expressions/concepts/forms/shared.rs @@ -0,0 +1,16 @@ +use super::*; + +type Shared = Actual<'static, T, BeShared>; + +pub(crate) struct BeShared; +impl IsForm for BeShared { + type Leaf<'a, T: IsValueLeaf> = SharedSubRcRefCell; + type DynLeaf<'a, T: 'static + ?Sized> = SharedSubRcRefCell; + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; + + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> { + leaf.map_optional(T::map_ref) + } +} diff --git a/src/expressions/concepts/mod.rs b/src/expressions/concepts/mod.rs new file mode 100644 index 00000000..17ddf9f8 --- /dev/null +++ b/src/expressions/concepts/mod.rs @@ -0,0 +1,16 @@ +#![allow(dead_code)] // Whilst we're building it out +use super::*; + +mod actual; +mod dyn_impls; +mod form; +mod forms; +mod type_impls; +mod type_traits; + +pub(crate) use actual::*; +pub(crate) use dyn_impls::*; +pub(crate) use form::*; +pub(crate) use forms::*; +pub(crate) use type_impls::*; +pub(crate) use type_traits::*; diff --git a/src/expressions/concepts/type_impls.rs b/src/expressions/concepts/type_impls.rs new file mode 100644 index 00000000..1a49e396 --- /dev/null +++ b/src/expressions/concepts/type_impls.rs @@ -0,0 +1,172 @@ +use super::*; + +// NOTE: These should be moved out to the value definitions soon + +pub(crate) struct ValueType; + +// type Value = Actual<'static, ValueType, BeOwned>; +// type ValueReferencable = Actual<'static, ValueType, BeReferencable>; +// type ValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; +// type ValueMut<'a> = Actual<'a, ValueType, BeAnyRefMut>; + +impl MapToType for ValueType { + fn map_to_type<'a>(content: Self::Content<'a, F>) -> Self::Content<'a, F> { + content + } +} + +impl MaybeMapFromType for ValueType { + fn maybe_map_from_type<'a>(content: Self::Content<'a, F>) -> Option> { + Some(content) + } +} + +impl IsType for ValueType { + type Content<'a, F: IsForm> = ValueContent<'a, F>; + + fn articled_type_name() -> &'static str { + "any value" + } +} + +impl IsHierarchyType for ValueType { + fn map_with<'a, F: IsForm, M: GeneralMapper>( + content: Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>> { + match content { + ValueContent::Integer(x) => Ok(ValueContent::Integer(x.map_with::()?)), + ValueContent::Object(x) => Ok(ValueContent::Object(x.map_with::()?)), + } + } +} + +pub(crate) enum ValueContent<'a, F: IsForm> { + Integer(Actual<'a, IntegerType, F>), + Object(Actual<'a, ObjectType, F>), + // ... +} + +pub(crate) enum IntegerContent<'a, F: IsForm> { + U32(Actual<'a, U32Type, F>), + U64(Actual<'a, U64Type, F>), + // ... +} + +pub(crate) struct IntegerType; + +impl IsType for IntegerType { + type Content<'a, F: IsForm> = IntegerContent<'a, F>; + + fn articled_type_name() -> &'static str { + "an integer" + } +} + +impl IsHierarchyType for IntegerType { + fn map_with<'a, F: IsForm, M: GeneralMapper>( + content: Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>> { + match content { + IntegerContent::U32(x) => Ok(IntegerContent::U32(x.map_with::()?)), + IntegerContent::U64(x) => Ok(IntegerContent::U64(x.map_with::()?)), + } + } +} + +impl_ancestor_chain_conversions!( + IntegerType => ValueType => [] +); + +impl IsChildType for IntegerType { + type ParentType = ValueType; + + fn into_parent<'a, F: IsForm>( + content: Self::Content<'a, F>, + ) -> ::Content<'a, F> { + ValueContent::Integer(Actual(content)) + } + + fn from_parent<'a, F: IsForm>( + content: ::Content<'a, F>, + ) -> Option> { + match content { + ValueContent::Integer(i) => Some(i.0), + _ => None, + } + } +} + +define_leaf_type!(u32: pub(crate) U32Type "a u32"); + +impl_ancestor_chain_conversions!( + U32Type => IntegerType => [ValueType] +); + +impl IsChildType for U32Type { + type ParentType = IntegerType; + + fn into_parent<'a, F: IsForm>( + content: Self::Content<'a, F>, + ) -> ::Content<'a, F> { + IntegerContent::U32(Actual(content)) + } + + fn from_parent<'a, F: IsForm>( + content: ::Content<'a, F>, + ) -> Option> { + match content { + IntegerContent::U32(i) => Some(i.0), + _ => None, + } + } +} + +define_leaf_type!(u64: pub(crate) U64Type "a u64"); + +impl_ancestor_chain_conversions!( + U64Type => IntegerType => [ValueType] +); + +impl IsChildType for U64Type { + type ParentType = IntegerType; + + fn into_parent<'a, F: IsForm>( + content: Self::Content<'a, F>, + ) -> ::Content<'a, F> { + IntegerContent::U64(Actual(content)) + } + + fn from_parent<'a, F: IsForm>( + content: ::Content<'a, F>, + ) -> Option> { + match content { + IntegerContent::U64(i) => Some(i.0), + _ => None, + } + } +} + +define_leaf_type!(ObjectValue: pub(crate) ObjectType "an object"); + +impl_ancestor_chain_conversions!( + ObjectType => ValueType => [] +); + +impl IsChildType for ObjectType { + type ParentType = ValueType; + + fn into_parent<'a, F: IsForm>( + content: Self::Content<'a, F>, + ) -> ::Content<'a, F> { + ValueContent::Object(Actual(content)) + } + + fn from_parent<'a, F: IsForm>( + content: ::Content<'a, F>, + ) -> Option> { + match content { + ValueContent::Object(o) => Some(o.0), + _ => None, + } + } +} diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs new file mode 100644 index 00000000..74b4485d --- /dev/null +++ b/src/expressions/concepts/type_traits.rs @@ -0,0 +1,200 @@ +use super::*; + +pub(crate) trait IsType: Sized { + type Content<'a, F: IsForm>; + + fn articled_type_name() -> &'static str; +} + +pub(crate) trait IsHierarchyType: IsType { + fn map_with<'a, F: IsForm, M: GeneralMapper>( + content: Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>>; +} + +pub(crate) trait IsDynType: IsType +where + DynMapper: GeneralMapper, +{ + type DynContent: ?Sized; +} + +pub(crate) trait GeneralMapper { + type OutputForm: IsForm; + type ShortCircuit<'a>; + + fn map_leaf<'a, L: IsValueLeaf, T>( + leaf: F::Leaf<'a, L>, + ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> + where + T: for<'l> IsType = F::Leaf<'l, L>>, + T: for<'l> IsType< + Content<'l, Self::OutputForm> = ::Leaf<'l, L>, + >; +} + +pub(crate) trait MapToType: IsType { + fn map_to_type<'a>(content: Self::Content<'a, F>) -> T::Content<'a, F>; +} + +pub(crate) trait MaybeMapFromType: IsType { + fn maybe_map_from_type<'a>(content: T::Content<'a, F>) -> Option>; + + fn resolve<'a>( + actual: Actual<'a, T, F>, + span_range: SpanRange, + resolution_target: &str, + ) -> ExecutionResult> { + let content = match Self::maybe_map_from_type(actual.0) { + Some(c) => c, + None => { + return span_range.value_err(format!( + "{} is expected to be {}, but it is {}", + resolution_target, + Self::articled_type_name(), + T::articled_type_name(), + )) + } + }; + Ok(Actual(content)) + } +} + +pub(crate) trait IsChildType: IsType { + type ParentType: IsType; + fn into_parent<'a, F: IsForm>( + content: Self::Content<'a, F>, + ) -> ::Content<'a, F>; + fn from_parent<'a, F: IsForm>( + content: ::Content<'a, F>, + ) -> Option>; +} + +macro_rules! impl_ancestor_chain_conversions { + ($child:ty => $parent:ty => [$($ancestor:ty),* $(,)?]) => { + impl MaybeMapFromType<$child, F> for $child + { + fn maybe_map_from_type<'a>( + content: <$child as IsType>::Content<'a, F>, + ) -> Option<<$child as IsType>::Content<'a, F>> { + Some(content) + } + } + + impl MapToType<$child, F> for $child + { + fn map_to_type<'a>( + content: <$child as IsType>::Content<'a, F>, + ) -> <$child as IsType>::Content<'a, F> { + content + } + } + + impl MaybeMapFromType<$parent, F> for $child + { + fn maybe_map_from_type<'a>( + content: <$parent as IsType>::Content<'a, F>, + ) -> Option<<$child as IsType>::Content<'a, F>> { + <$child as IsChildType>::from_parent(content) + } + } + + impl MapToType<$parent, F> for $child + { + fn map_to_type<'a>( + content: <$child as IsType>::Content<'a, F>, + ) -> <$parent as IsType>::Content<'a, F> { + <$child as IsChildType>::into_parent(content) + } + } + + $( + impl MaybeMapFromType<$ancestor, F> for $child { + fn maybe_map_from_type<'a>( + content: <$ancestor as IsType>::Content<'a, F>, + ) -> Option<<$child as IsType>::Content<'a, F>> { + <$child as MaybeMapFromType<$parent, F>>::maybe_map_from_type(<$parent as MaybeMapFromType<$ancestor, F>>::maybe_map_from_type(content)?) + } + } + + impl MapToType<$ancestor, F> for $child { + fn map_to_type<'a>( + content: <$child as IsType>::Content<'a, F>, + ) -> <$ancestor as IsType>::Content<'a, F> { + <$parent as MapToType<$ancestor, F>>::map_to_type(<$child as MapToType<$parent, F>>::map_to_type(content)) + } + } + )* + }; +} + +pub(crate) use impl_ancestor_chain_conversions; + +pub(crate) trait IsValueLeaf: + 'static + IntoValueContent<'static, Form = BeOwned> + CastDyn +{ +} + +pub(crate) trait IsDynLeaf: 'static + IsValueContent<'static> +where + DynMapper: GeneralMapper, + Self::Type: IsDynType, +{ +} + +pub(crate) trait CastDyn { + fn map_boxed(self: Box) -> Option> { + None + } + fn map_ref(&self) -> Option<&T> { + None + } + fn map_mut(&mut self) -> Option<&mut T> { + None + } +} + +macro_rules! define_leaf_type { + ($leaf_type:ty : $type_data_vis:vis $leaf_type_def:ident $articled_type_name:literal) => { + $type_data_vis struct $leaf_type_def; + + impl IsType for $leaf_type_def { + type Content<'a, F: IsForm> = F::Leaf<'a, $leaf_type>; + + fn articled_type_name() -> &'static str { + $articled_type_name + } + } + + impl IsHierarchyType for $leaf_type_def { + fn map_with<'a, F: IsForm, M: GeneralMapper>( + content: Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>> { + M::map_leaf::<$leaf_type, Self>(content) + } + } + + impl<'a> IsValueContent<'a> for $leaf_type { + type Type = $leaf_type_def; + type Form = BeOwned; + } + + impl<'a> IntoValueContent<'a> for $leaf_type { + fn into_content(self) -> Self { + self + } + } + + impl<'a> FromValueContent<'a> for $leaf_type { + fn from_content(content: Self) -> Self { + content + } + } + + impl IsValueLeaf for $leaf_type {} + impl CastDyn for $leaf_type {} + }; + +} + +pub(crate) use define_leaf_type; diff --git a/src/expressions/mod.rs b/src/expressions/mod.rs index ff71f979..a2590409 100644 --- a/src/expressions/mod.rs +++ b/src/expressions/mod.rs @@ -1,3 +1,8 @@ +// Marked as use for expression sub-modules to use with a `use super::*` statement +use crate::internal_prelude::*; +use expression_parsing::*; + +mod concepts; mod control_flow; mod evaluation; mod expression; @@ -10,6 +15,8 @@ mod statements; mod type_resolution; mod values; +#[allow(unused_imports)] // Whilst we're building it out +pub(crate) use concepts::*; pub(crate) use control_flow::*; pub(crate) use evaluation::*; pub(crate) use expression::*; @@ -20,7 +27,3 @@ pub(crate) use patterns::*; pub(crate) use statements::*; pub(crate) use type_resolution::*; pub(crate) use values::*; - -// Marked as use for expression sub-modules to use with a `use super::*` statement -use crate::internal_prelude::*; -use expression_parsing::*; diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index 7ac2babd..0e836ecb 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -94,7 +94,7 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgument } } -impl IsArgument for AnyRefMut<'static, T> +impl IsArgument for AnyMut<'static, T> where Mutable: IsArgument, { diff --git a/src/expressions/type_resolution/interface_macros.rs b/src/expressions/type_resolution/interface_macros.rs index 69e0b3ca..39fc83b5 100644 --- a/src/expressions/type_resolution/interface_macros.rs +++ b/src/expressions/type_resolution/interface_macros.rs @@ -110,6 +110,9 @@ macro_rules! generate_method_interface { ], } }; + (REQUIRED $req:tt OPTIONAL $opt:tt ARGS[$($arg:tt)*]) => { + compile_error!(stringify!("This method arity is currently unsupported - add support in `MethodInterface` and `generate_method_interface`: ", $($arg)*)); + }; } macro_rules! parse_arg_types { @@ -123,12 +126,18 @@ macro_rules! parse_arg_types { parse_arg_types!([REQUIRED $req OPTIONAL[$($opt)* $type,] => $callback! $callback_args] $($rest)*) }; // Next tokens are `: X)` - we have a required argument (variant 1) + ([REQUIRED [$($req:tt)*] OPTIONAL [] => $callback:ident! $callback_args:tt] : $type:ty) => { + parse_arg_types!([REQUIRED[$($req)* $type,] OPTIONAL [] => $callback! $callback_args]) + }; ([REQUIRED [$($req:tt)*] OPTIONAL $opt:tt => $callback:ident! $callback_args:tt] : $type:ty) => { - parse_arg_types!([REQUIRED[$($req)* $type,] OPTIONAL $opt => $callback! $callback_args]) + compile_error!(stringify!("Required arguments must come before optional arguments:" $type)); }; // Next tokens are `: X, ...` - we have a required argument (variant 2) + ([REQUIRED [$($req:tt)*] OPTIONAL [] => $callback:ident! $callback_args:tt] : $type:ty, $($rest:tt)*) => { + parse_arg_types!([REQUIRED[$($req)* $type,] OPTIONAL [] => $callback! $callback_args] $($rest)*) + }; ([REQUIRED [$($req:tt)*] OPTIONAL $opt:tt => $callback:ident! $callback_args:tt] : $type:ty, $($rest:tt)*) => { - parse_arg_types!([REQUIRED[$($req)* $type,] OPTIONAL $opt => $callback! $callback_args] $($rest)*) + compile_error!(stringify!("Required arguments must come before optional arguments:" $type)); }; // Next tokens are something else - ignore it and look at next ([REQUIRED $req:tt OPTIONAL $opt:tt => $callback:ident! $callback_args:tt] $consumed:tt $($rest:tt)*) => { diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index c50f0236..a0fe5a2a 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -356,7 +356,7 @@ impl DerefMut for Assignee { /// Can be destructured as: `Mutable(cell): Mutable` /// /// If you need span information, wrap with `Spanned>`. -pub(crate) struct Mutable(pub(crate) MutSubRcRefCell); +pub(crate) struct Mutable(pub(crate) MutableSubRcRefCell); impl Mutable { pub(crate) fn into_shared(self) -> Shared { @@ -412,11 +412,11 @@ impl Spanned> { impl Mutable { pub(crate) fn new_from_owned(value: OwnedValue) -> Self { // Unwrap is safe because it's a new refcell - Mutable(MutSubRcRefCell::new(Rc::new(RefCell::new(value.0))).unwrap()) + Mutable(MutableSubRcRefCell::new(Rc::new(RefCell::new(value.0))).unwrap()) } fn new_from_variable(reference: VariableBinding) -> syn::Result { - Ok(Mutable(MutSubRcRefCell::new(reference.data).map_err( + Ok(Mutable(MutableSubRcRefCell::new(reference.data).map_err( |_| reference.variable_span.syn_error(MUTABLE_ERROR_MESSAGE), )?)) } diff --git a/src/interpretation/refs.rs b/src/interpretation/refs.rs index bc14d296..e9f61f46 100644 --- a/src/interpretation/refs.rs +++ b/src/interpretation/refs.rs @@ -8,6 +8,17 @@ pub(crate) struct AnyRef<'a, T: ?Sized + 'static> { impl<'a, T: ?Sized + 'static> AnyRef<'a, T> { #[allow(unused)] + pub(crate) fn map(self, f: impl for<'r> FnOnce(&'r T) -> &'r S) -> AnyRef<'a, S> { + match self.inner { + AnyRefInner::Direct(value) => AnyRef { + inner: AnyRefInner::Direct(f(value)), + }, + AnyRefInner::Encapsulated(shared) => AnyRef { + inner: AnyRefInner::Encapsulated(shared.map(|x| f(x))), + }, + } + } + pub(crate) fn map_optional( self, f: impl for<'r> FnOnce(&'r T) -> Option<&'r S>, @@ -17,7 +28,7 @@ impl<'a, T: ?Sized + 'static> AnyRef<'a, T> { inner: AnyRefInner::Direct(f(value)?), }, AnyRefInner::Encapsulated(shared) => AnyRef { - inner: AnyRefInner::Encapsulated(shared.try_map(|x| f(x).ok_or(())).ok()?), + inner: AnyRefInner::Encapsulated(shared.map_optional(f)?), }, }) } @@ -83,61 +94,92 @@ impl<'a, T: 'static + ?Sized> Deref for AnyRef<'a, T> { /// A flexible type which can either be a mutable reference to a value of type `T`, /// or an encapsulated reference from a [`Mutable`]. -pub(crate) struct AnyRefMut<'a, T: 'static + ?Sized> { - inner: AnyRefMutInner<'a, T>, +pub(crate) struct AnyMut<'a, T: 'static + ?Sized> { + inner: AnyMutInner<'a, T>, } -impl<'a, T: ?Sized> From<&'a mut T> for AnyRefMut<'a, T> { +impl<'a, T: ?Sized> From<&'a mut T> for AnyMut<'a, T> { fn from(value: &'a mut T) -> Self { Self { - inner: AnyRefMutInner::Direct(value), + inner: AnyMutInner::Direct(value), } } } +impl<'a, T: ?Sized + 'static> AnyMut<'a, T> { + #[allow(unused)] + pub(crate) fn map( + self, + f: impl for<'r> FnOnce(&'r mut T) -> &'r mut S, + ) -> AnyMut<'a, S> { + match self.inner { + AnyMutInner::Direct(value) => AnyMut { + inner: AnyMutInner::Direct(f(value)), + }, + AnyMutInner::Encapsulated(mutable) => AnyMut { + inner: AnyMutInner::Encapsulated(mutable.map(|x| f(x))), + }, + } + } + + pub(crate) fn map_optional( + self, + f: impl for<'r> FnOnce(&'r mut T) -> Option<&'r mut S>, + ) -> Option> { + Some(match self.inner { + AnyMutInner::Direct(value) => AnyMut { + inner: AnyMutInner::Direct(f(value)?), + }, + AnyMutInner::Encapsulated(mutable) => AnyMut { + inner: AnyMutInner::Encapsulated(mutable.map_optional(f)?), + }, + }) + } +} + #[allow(unused)] -pub(crate) trait IntoRefMut<'a> { +pub(crate) trait IntoAnyMut<'a> { type Target: ?Sized; - fn into_ref_mut(self) -> AnyRefMut<'a, Self::Target>; + fn into_any_mut(self) -> AnyMut<'a, Self::Target>; } -impl<'a, T: ?Sized + 'static> IntoRefMut<'a> for &'a mut T { +impl<'a, T: ?Sized + 'static> IntoAnyMut<'a> for &'a mut T { type Target = T; - fn into_ref_mut(self) -> AnyRefMut<'a, Self::Target> { + fn into_any_mut(self) -> AnyMut<'a, Self::Target> { self.into() } } -impl<'a, T: ?Sized> From> for AnyRefMut<'a, T> { +impl<'a, T: ?Sized> From> for AnyMut<'a, T> { fn from(value: Mutable) -> Self { Self { - inner: AnyRefMutInner::Encapsulated(value.0), + inner: AnyMutInner::Encapsulated(value.0), } } } -enum AnyRefMutInner<'a, T: 'static + ?Sized> { +enum AnyMutInner<'a, T: 'static + ?Sized> { Direct(&'a mut T), - Encapsulated(MutSubRcRefCell), + Encapsulated(MutableSubRcRefCell), } -impl<'a, T: 'static + ?Sized> Deref for AnyRefMut<'a, T> { +impl<'a, T: 'static + ?Sized> Deref for AnyMut<'a, T> { type Target = T; fn deref(&self) -> &T { match &self.inner { - AnyRefMutInner::Direct(value) => value, - AnyRefMutInner::Encapsulated(shared) => shared, + AnyMutInner::Direct(value) => value, + AnyMutInner::Encapsulated(shared) => shared, } } } -impl<'a, T: 'static + ?Sized> DerefMut for AnyRefMut<'a, T> { +impl<'a, T: 'static + ?Sized> DerefMut for AnyMut<'a, T> { fn deref_mut(&mut self) -> &mut T { match &mut self.inner { - AnyRefMutInner::Direct(value) => value, - AnyRefMutInner::Encapsulated(shared) => &mut *shared, + AnyMutInner::Direct(value) => value, + AnyMutInner::Encapsulated(shared) => &mut *shared, } } } diff --git a/src/misc/mut_rc_ref_cell.rs b/src/misc/mut_rc_ref_cell.rs index ff354705..74e10e7f 100644 --- a/src/misc/mut_rc_ref_cell.rs +++ b/src/misc/mut_rc_ref_cell.rs @@ -2,8 +2,8 @@ use crate::internal_prelude::*; use std::cell::{BorrowError, BorrowMutError}; /// A mutable reference to a sub-value `U` inside a [`Rc>`]. -/// Only one [`MutSubRcRefCell`] can exist at a time for a given [`Rc>`]. -pub(crate) struct MutSubRcRefCell { +/// Only one [`MutableSubRcRefCell`] can exist at a time for a given [`Rc>`]. +pub(crate) struct MutableSubRcRefCell { /// This is actually a reference to the contents of the RefCell /// but we store it using `unsafe` as `'static`, and use unsafe blocks /// to ensure it's dropped first. @@ -13,7 +13,7 @@ pub(crate) struct MutSubRcRefCell { pointed_at: Rc>, } -impl MutSubRcRefCell { +impl MutableSubRcRefCell { pub(crate) fn new(pointed_at: Rc>) -> Result { let ref_mut = pointed_at.try_borrow_mut()?; Ok(Self { @@ -31,14 +31,14 @@ impl MutSubRcRefCell { } } -impl MutSubRcRefCell { +impl MutableSubRcRefCell { pub(crate) fn into_shared(self) -> SharedSubRcRefCell { let ptr = self.ref_mut.deref() as *const U; drop(self.ref_mut); // SAFETY: // - The pointer was previously a reference, so it is safe to deference it here // (the pointer is pointing into the Rc> which hasn't moved) - // - All our invariants for SharedSubRcRefCell / MutSubRcRefCell are maintained + // - All our invariants for SharedSubRcRefCell / MutableSubRcRefCell are maintained unsafe { // The unwrap cannot panic because we just held a mutable borrow, we're not in Sync land, so no-one else can have a borrow. SharedSubRcRefCell::new(self.pointed_at) @@ -72,17 +72,30 @@ impl MutSubRcRefCell { } } - pub(crate) fn map(self, f: impl FnOnce(&mut U) -> &mut V) -> MutSubRcRefCell { - MutSubRcRefCell { + pub(crate) fn map( + self, + f: impl FnOnce(&mut U) -> &mut V, + ) -> MutableSubRcRefCell { + MutableSubRcRefCell { ref_mut: RefMut::map(self.ref_mut, f), pointed_at: self.pointed_at, } } + pub(crate) fn map_optional( + self, + f: impl FnOnce(&mut U) -> Option<&mut V>, + ) -> Option> { + Some(MutableSubRcRefCell { + ref_mut: RefMut::filter_map(self.ref_mut, f).ok()?, + pointed_at: self.pointed_at, + }) + } + pub(crate) fn try_map( self, f: impl FnOnce(&mut U) -> Result<&mut V, E>, - ) -> Result, E> { + ) -> Result, E> { let mut error = None; let outcome = RefMut::filter_map(self.ref_mut, |inner| match f(inner) { Ok(value) => Some(value), @@ -92,7 +105,7 @@ impl MutSubRcRefCell { } }); match outcome { - Ok(ref_mut) => Ok(MutSubRcRefCell { + Ok(ref_mut) => Ok(MutableSubRcRefCell { ref_mut, pointed_at: self.pointed_at, }), @@ -101,13 +114,13 @@ impl MutSubRcRefCell { } } -impl DerefMut for MutSubRcRefCell { +impl DerefMut for MutableSubRcRefCell { fn deref_mut(&mut self) -> &mut U { &mut self.ref_mut } } -impl Deref for MutSubRcRefCell { +impl Deref for MutableSubRcRefCell { type Target = U; fn deref(&self) -> &U { &self.ref_mut @@ -116,7 +129,7 @@ impl Deref for MutSubRcRefCell { /// A shared (immutable) reference to a sub-value `U` inside a [`Rc>`]. /// Many [`SharedSubRcRefCell`] can exist at the same time for a given [`Rc>`], -/// but if any exist, then no [`MutSubRcRefCell`] can exist. +/// but if any exist, then no [`MutableSubRcRefCell`] can exist. pub(crate) struct SharedSubRcRefCell { /// This is actually a reference to the contents of the RefCell /// but we store it using `unsafe` as `'static`, and use unsafe blocks @@ -156,6 +169,16 @@ impl SharedSubRcRefCell { } } + pub(crate) fn map_optional( + self, + f: impl FnOnce(&U) -> Option<&V>, + ) -> Option> { + Some(SharedSubRcRefCell { + shared_ref: Ref::filter_map(self.shared_ref, f).ok()?, + pointed_at: self.pointed_at, + }) + } + pub(crate) fn try_map( self, f: impl FnOnce(&U) -> Result<&V, E>, diff --git a/src/sandbox/dyn_value.rs b/src/sandbox/dyn_value.rs index 990c3603..91a6645f 100644 --- a/src/sandbox/dyn_value.rs +++ b/src/sandbox/dyn_value.rs @@ -1,5 +1,9 @@ #![allow(unused)] +// NOTE: This was an alternative design for modelling the type structure +// But we ended up going with enums/GATs instead for simplicity +// Non-hierarchical types such as "Iterable" are defined as `Box` though + use std::any::Any; trait IsValue: Any { type TypeData: 'static + TypeData; diff --git a/src/sandbox/gat_value.rs b/src/sandbox/gat_value.rs deleted file mode 100644 index c82d85fd..00000000 --- a/src/sandbox/gat_value.rs +++ /dev/null @@ -1,662 +0,0 @@ -#![allow(unused)] - -// SUMMARY: -// - Strip SpanRange out of Owned/Shared etc. -// - Instead, have Spanned wrapper type, which can be destructured with Spanned(value, span) -// - Implement something akin to the below: -// - `type Owned = Actual<'static, T, BeOwned>` -// - `type CopyOnWrite = Actual<'static, T, BeCopyOnWrite>` -// - `type Shared = Actual<'static, T, BeShared>` -// -// - For mapping outputs to methods/functions, we can do: -// `>::into_actual().map_type::().into_returned_value()` -// - For mapping arguments to methods/functions, we can use the `IsArgument` implementation below - -use crate::internal_prelude::*; - -trait IsOwnership: Sized { - type Leaf<'a, T: IsLeaf>; - type DynLeaf<'a, T: 'static + ?Sized>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult>; -} - -trait IsLeaf: 'static { - fn into_iterable(self: Box) -> Option> { - None - } - fn as_iterable(&self) -> Option<&dyn IsIterable> { - None - } -} - -struct BeOwned; -impl IsOwnership for BeOwned { - type Leaf<'a, T: IsLeaf> = T; - type DynLeaf<'a, T: 'static + ?Sized> = Box; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult> { - // value.expect_owned() - todo!() - } -} - -// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. -struct BeReferencable; -impl IsOwnership for BeReferencable { - type Leaf<'a, T: IsLeaf> = Rc>; - type DynLeaf<'a, T: 'static + ?Sized> = Rc>; - - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult> { - // Rc::new(RefCell::new(value.expect_owned())) - todo!() - } -} - -struct BeAnyRef; -impl IsOwnership for BeAnyRef { - type Leaf<'a, T: IsLeaf> = AnyRef<'a, T>; - type DynLeaf<'a, T: 'static + ?Sized> = AnyRef<'a, T>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult> { - todo!() - } -} - -struct BeAnyRefMut; -impl IsOwnership for BeAnyRefMut { - type Leaf<'a, T: IsLeaf> = AnyRefMut<'a, T>; - type DynLeaf<'a, T: 'static + ?Sized> = AnyRefMut<'a, T>; - - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult> { - todo!() - } -} - -struct OwnedToReferencableMapper; - -trait OwnershipMapper { - type HFrom: IsOwnership; - type HTo: IsOwnership; - - fn map_leaf<'a, T: IsLeaf>( - leaf: ::Leaf<'a, T>, - ) -> ::Leaf<'a, T>; -} - -impl OwnershipMapper for OwnedToReferencableMapper { - type HFrom = BeOwned; - type HTo = BeReferencable; - - fn map_leaf<'a, T: IsLeaf>( - leaf: ::Leaf<'a, T>, - ) -> ::Leaf<'a, T> { - Rc::new(RefCell::new(leaf)) - } -} - -trait IsType: Sized { - type Content<'a, H: IsOwnership>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo>; - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a>; - - fn type_name() -> &'static str { - todo!() - } -} - -trait MapToType: IsType { - fn map_to_type<'a>(content: Self::Content<'a, H>) -> T::Content<'a, H>; -} - -trait MaybeMapFromType: IsType { - fn maybe_map_from_type<'a>(content: T::Content<'a, H>) -> Option>; - - fn resolve<'a>( - actual: Actual<'a, T, H>, - span_range: SpanRange, - context: &str, - ) -> ExecutionResult> { - let content = match Self::maybe_map_from_type(actual.0) { - Some(c) => c, - None => { - return span_range.value_err(format!( - "{} cannot be mapped to {}", - context, - T::type_name(), - )) - } - }; - Ok(Actual(content)) - } -} - -trait IsChildType: IsType { - type ParentType: IsType; - fn into_parent<'a, H: IsOwnership>( - content: Self::Content<'a, H>, - ) -> ::Content<'a, H>; - fn from_parent<'a, H: IsOwnership>( - content: ::Content<'a, H>, - ) -> Option>; -} - -macro_rules! impl_ancestor_chain_conversions { - ($child:ty => $parent:ty => [$($ancestor:ty),* $(,)?]) => { - impl MaybeMapFromType<$child, H> for $child - { - fn maybe_map_from_type<'a>( - content: <$child as IsType>::Content<'a, H>, - ) -> Option<<$child as IsType>::Content<'a, H>> { - Some(content) - } - } - - impl MapToType<$child, H> for $child - { - fn map_to_type<'a>( - content: <$child as IsType>::Content<'a, H>, - ) -> <$child as IsType>::Content<'a, H> { - content - } - } - - impl MaybeMapFromType<$parent, H> for $child - { - fn maybe_map_from_type<'a>( - content: <$parent as IsType>::Content<'a, H>, - ) -> Option<<$child as IsType>::Content<'a, H>> { - <$child as IsChildType>::from_parent(content) - } - } - - impl MapToType<$parent, H> for $child - { - fn map_to_type<'a>( - content: <$child as IsType>::Content<'a, H>, - ) -> <$parent as IsType>::Content<'a, H> { - <$child as IsChildType>::into_parent(content) - } - } - - $( - impl MaybeMapFromType<$ancestor, H> for $child { - fn maybe_map_from_type<'a>( - content: <$ancestor as IsType>::Content<'a, H>, - ) -> Option<<$child as IsType>::Content<'a, H>> { - <$child as MaybeMapFromType<$parent, H>>::maybe_map_from_type(<$parent as MaybeMapFromType<$ancestor, H>>::maybe_map_from_type(content)?) - } - } - - impl MapToType<$ancestor, H> for $child { - fn map_to_type<'a>( - content: <$child as IsType>::Content<'a, H>, - ) -> <$ancestor as IsType>::Content<'a, H> { - <$parent as MapToType<$ancestor, H>>::map_to_type(<$child as MapToType<$parent, H>>::map_to_type(content)) - } - } - )* - }; -} - -struct ValueType; - -impl MapToType for ValueType { - fn map_to_type<'a>(content: Self::Content<'a, H>) -> Self::Content<'a, H> { - content - } -} - -impl MaybeMapFromType for ValueType { - fn maybe_map_from_type<'a>(content: Self::Content<'a, H>) -> Option> { - Some(content) - } -} - -impl IsType for ValueType { - type Content<'a, H: IsOwnership> = ValueContent<'a, H>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - match content { - ValueContent::Integer(x) => ValueContent::Integer(x.map_ownership::()), - ValueContent::Object(x) => ValueContent::Object(x.map_ownership::()), - } - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - match content { - ValueContent::Integer(x) => x.map_content::(), - ValueContent::Object(x) => x.map_content::(), - } - } -} - -enum ValueContent<'a, H: IsOwnership> { - Integer(Actual<'a, IntegerType, H>), - Object(Actual<'a, ObjectType, H>), - // ... -} - -struct Actual<'a, K: IsType, H: IsOwnership>(K::Content<'a, H>); - -impl<'a, K: IsType, H: IsOwnership> Actual<'a, K, H> { - #[inline] - fn of(content: impl IntoValueContent<'a, TypeData = K, Ownership = H>) -> Self { - Actual(content.into_content()) - } - - #[inline] - fn map_ownership>(self) -> Actual<'a, K, M::HTo> { - Actual(K::map_ownership::(self.0)) - } - - #[inline] - fn map_content>(self) -> M::TOut<'a> { - K::map_content::<'a, H, M>(self.0) - } - - fn map_type(self) -> Actual<'a, U, H> - where - K: MapToType, - { - Actual(K::map_to_type(self.0)) - } - - fn map_type_maybe>(self) -> Option> { - Some(Actual(U::maybe_map_from_type(self.0)?)) - } -} - -impl<'a, K: IsType, H: IsOwnership> Spanned> { - fn resolve_as>( - self, - description: &str, - ) -> ExecutionResult> { - let Spanned(value, span_range) = self; - U::resolve(value, span_range, description) - } -} - -impl<'a, K: IsType> Actual<'a, K, BeOwned> { - fn into_referencable(self) -> Actual<'a, K, BeReferencable> { - self.map_ownership::() - } -} - -impl<'a, K: IsType + MapToType, H: IsOwnership> Actual<'a, K, H> { - fn into_value(self) -> Actual<'a, ValueType, H> { - self.map_type() - } -} - -type Value = Actual<'static, ValueType, BeOwned>; -type ValueReferencable = Actual<'static, ValueType, BeReferencable>; -type ValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; -type ValueMut<'a> = Actual<'a, ValueType, BeAnyRefMut>; - -struct ObjectValue; - -impl IsLeaf for ObjectValue {} - -struct ObjectType; - -impl IsType for ObjectType { - type Content<'a, H: IsOwnership> = H::Leaf<'a, ObjectValue>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - M::map_leaf(content) - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - M::map_leaf(content) - } -} - -impl_ancestor_chain_conversions!( - ObjectType => ValueType => [] -); - -impl IsChildType for ObjectType { - type ParentType = ValueType; - - fn into_parent<'a, H: IsOwnership>( - content: Self::Content<'a, H>, - ) -> ::Content<'a, H> { - ValueContent::Object(Actual(content)) - } - - fn from_parent<'a, H: IsOwnership>( - content: ::Content<'a, H>, - ) -> Option> { - match content { - ValueContent::Object(o) => Some(o.0), - _ => None, - } - } -} - -enum IntegerContent<'a, H: IsOwnership> { - U32(Actual<'a, U32Type, H>), - // ... -} - -struct IntegerType; - -impl IsType for IntegerType { - type Content<'a, H: IsOwnership> = IntegerContent<'a, H>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - match content { - IntegerContent::U32(i) => IntegerContent::U32(i.map_ownership::()), - } - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - match content { - IntegerContent::U32(x) => x.map_content::(), - } - } -} - -impl_ancestor_chain_conversions!( - IntegerType => ValueType => [] -); - -impl IsChildType for IntegerType { - type ParentType = ValueType; - - fn into_parent<'a, H: IsOwnership>( - content: Self::Content<'a, H>, - ) -> ::Content<'a, H> { - ValueContent::Integer(Actual(content)) - } - - fn from_parent<'a, H: IsOwnership>( - content: ::Content<'a, H>, - ) -> Option> { - match content { - ValueContent::Integer(i) => Some(i.0), - _ => None, - } - } -} - -impl IsLeaf for u32 { - fn into_iterable(self: Box) -> Option> { - Some(self) - } - fn as_iterable(&self) -> Option<&dyn IsIterable> { - Some(self) - } -} - -impl IsIterable for u32 { - fn into_iterator(self: Box) -> ExecutionResult { - Ok(IteratorValue::new_any(std::iter::once( - (*self).into_value(), - ))) - } - - fn len(&self, error_span_range: SpanRange) -> ExecutionResult { - Ok(0) - } -} - -struct U32Type; - -impl IsType for U32Type { - type Content<'a, H: IsOwnership> = H::Leaf<'a, u32>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - M::map_leaf(content) - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - M::map_leaf(content) - } -} - -impl IsChildType for U32Type { - type ParentType = IntegerType; - - fn into_parent<'a, H: IsOwnership>( - content: Self::Content<'a, H>, - ) -> ::Content<'a, H> { - IntegerContent::U32(Actual(content)) - } - - fn from_parent<'a, H: IsOwnership>( - content: ::Content<'a, H>, - ) -> Option> { - match content { - IntegerContent::U32(i) => Some(i.0), - _ => None, - } - } -} - -impl_ancestor_chain_conversions!( - U32Type => IntegerType => [ValueType] -); - -#[test] -fn test() { - let my_u32 = Actual::of(42u32); - let as_value = my_u32.map_type::(); - let back_to_u32 = as_value.map_type_maybe::().unwrap(); - assert_eq!(back_to_u32.0, 42u32); -} - -// Clashes with other blanket trait it will replace! -// -// impl< -// X: FromValueContent<'static, TypeData = T, Ownership = H>, -// T: MaybeMapFromType + HierarchicalTypeData, -// H: IsOwnership, -// > IsArgument for X { -// type ValueType = T; -// const OWNERSHIP: ArgumentOwnership = H::ARGUMENT_OWNERSHIP; - -// fn from_argument(value: ArgumentValue) -> ExecutionResult { -// let span_range = value.span_range(); -// let ownership_mapped = H::from_argument_value(value)?; -// let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; -// Ok(X::from_actual(type_mapped)) -// } -// } - -trait IsValueContent<'a> { - type TypeData: IsType; - type Ownership: IsOwnership; -} - -trait IntoValueContent<'a>: IsValueContent<'a> { - fn into_content(self) -> ::Content<'a, Self::Ownership>; - - #[inline] - fn into_actual(self) -> Actual<'a, Self::TypeData, Self::Ownership> - where - Self: Sized, - { - Actual::of(self) - } -} - -trait FromValueContent<'a>: IsValueContent<'a> { - fn from_content(content: ::Content<'a, Self::Ownership>) -> Self; - - #[inline] - fn from_actual(actual: Actual<'a, Self::TypeData, Self::Ownership>) -> Self - where - Self: Sized, - { - Self::from_content(actual.0) - } -} - -impl<'a, T: IsType, H: IsOwnership> IsValueContent<'a> for Actual<'a, T, H> { - type TypeData = T; - type Ownership = H; -} - -impl<'a, T: IsType, H: IsOwnership> IntoValueContent<'a> for Actual<'a, T, H> { - fn into_content(self) -> ::Content<'a, Self::Ownership> { - self.0 - } -} - -impl<'a, T: IsType, H: IsOwnership> FromValueContent<'a> for Actual<'a, T, H> { - fn from_content(content: ::Content<'a, Self::Ownership>) -> Self { - Actual(content) - } -} - -impl<'a> IsValueContent<'a> for u32 { - type TypeData = U32Type; - type Ownership = BeOwned; -} - -impl<'a> IntoValueContent<'a> for u32 { - fn into_content(self) -> u32 { - self - } -} - -impl<'a> FromValueContent<'a> for u32 { - fn from_content(content: u32) -> Self { - content - } -} - -impl<'a, H: IsOwnership> IsValueContent<'a> for ValueContent<'a, H> { - type TypeData = ValueType; - type Ownership = H; -} - -impl<'a, H: IsOwnership> IntoValueContent<'a> for ValueContent<'a, H> { - fn into_content(self) -> ::Content<'a, Self::Ownership> { - self - } -} - -struct IterableType; - -trait IsIterable: 'static { - fn into_iterator(self: Box) -> ExecutionResult; - fn len(&self, error_span_range: SpanRange) -> ExecutionResult; -} - -impl IsType for IterableType { - type Content<'a, H: IsOwnership> = H::DynLeaf<'a, dyn IsIterable>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - // Might need to create separate IsMappableType trait and not impl it for dyn types - // And/or have a separate dyn mapping facility if we need it - todo!(); - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - // Might need to create separate IsMappableType trait and not impl it for dyn types - // And/or have a separate dyn mapping facility if we need it - todo!() - } -} - -impl MaybeMapFromType for IterableType { - fn maybe_map_from_type<'a>( - content: ::Content<'a, BeOwned>, - ) -> Option> { - T::map_content::<'a, BeOwned, IterableMapper>(content) - } -} - -impl MaybeMapFromType for IterableType { - fn maybe_map_from_type<'a>( - content: ::Content<'a, BeAnyRef>, - ) -> Option> { - T::map_content::<'a, BeAnyRef, IterableMapper>(content) - } -} - -trait ContentMapper { - type TOut<'a>; - - fn map_leaf<'a, L: IsLeaf>(leaf: H::Leaf<'a, L>) -> Self::TOut<'a>; -} - -struct IterableMapper; - -impl ContentMapper for IterableMapper { - type TOut<'a> = Option>; - - fn map_leaf<'a, L: IsLeaf>(leaf: L) -> Self::TOut<'a> { - L::into_iterable(Box::new(leaf)) - } -} - -impl ContentMapper for IterableMapper { - type TOut<'a> = Option>; - - fn map_leaf<'a, L: IsLeaf>(leaf: ::Leaf<'a, L>) -> Self::TOut<'a> { - leaf.map_optional(|x| L::as_iterable(x)) - } -} - -#[test] -fn test_iterable_mapping() { - let my_value = Actual::of(42u32); - // let my_value = my_value.map_type::(); - let as_iterable = my_value.map_type_maybe::().unwrap(); - let iterator = as_iterable.0.into_iterator().unwrap(); - let collected: Vec = iterator - .map(|v| { - Spanned(Owned::new(v), Span::call_site().span_range()) - .resolve_as("u32") - .unwrap() - }) - .collect(); - assert_eq!(collected, vec![42u32]); -} From df5fa8a2221fe36cb2adb714cee1535c9db173ab Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 16 Dec 2025 00:00:07 +0000 Subject: [PATCH 02/58] refactor: Small tweaks/fixes --- src/expressions/concepts/actual.rs | 20 +++++++------ src/expressions/concepts/dyn_impls.rs | 6 ++-- src/expressions/concepts/form.rs | 6 ++-- src/expressions/concepts/forms/owned.rs | 10 +++++++ src/expressions/concepts/type_impls.rs | 22 +++++++------- src/expressions/concepts/type_traits.rs | 38 ++++++++++++------------- 6 files changed, 57 insertions(+), 45 deletions(-) diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs index 38851213..7b524707 100644 --- a/src/expressions/concepts/actual.rs +++ b/src/expressions/concepts/actual.rs @@ -11,14 +11,14 @@ impl<'a, T: IsType, F: IsForm> Actual<'a, T, F> { #[inline] pub(crate) fn map_type(self) -> Actual<'a, S, F> where - T: MapToType, + T: UpcastTo, { - Actual(T::map_to_type(self.0)) + Actual(T::upcast_to(self.0)) } #[inline] - pub(crate) fn map_type_maybe>(self) -> Option> { - Some(Actual(U::maybe_map_from_type(self.0)?)) + pub(crate) fn map_type_maybe>(self) -> Option> { + Some(Actual(U::downcast_from(self.0)?)) } #[inline] @@ -34,16 +34,20 @@ impl<'a, T: IsType, F: IsForm> Actual<'a, T, F> { impl<'a, T: IsType, F: IsForm> Spanned> { #[inline] - pub(crate) fn resolve_as>( + pub(crate) fn resolve_as>( self, description: &str, - ) -> ExecutionResult> { + ) -> ExecutionResult + where + >::Type: DowncastFrom, + { let Spanned(value, span_range) = self; - U::resolve(value, span_range, description) + let resolved = <>::Type>::resolve(value, span_range, description)?; + Ok(X::from_actual(resolved)) } } -impl<'a, T: IsType + MapToType, F: IsForm> Actual<'a, T, F> { +impl<'a, T: IsType + UpcastTo, F: IsForm> Actual<'a, T, F> { pub(crate) fn into_value(self) -> Actual<'a, ValueType, F> { self.map_type() } diff --git a/src/expressions/concepts/dyn_impls.rs b/src/expressions/concepts/dyn_impls.rs index c812aa4c..6592a4b8 100644 --- a/src/expressions/concepts/dyn_impls.rs +++ b/src/expressions/concepts/dyn_impls.rs @@ -28,10 +28,8 @@ impl<'a> IsValueContent<'a> for dyn IsIterable { impl IsDynLeaf for dyn IsIterable {} -impl MaybeMapFromType for IterableType { - fn maybe_map_from_type<'a>( - content: ::Content<'a, F>, - ) -> Option> { +impl DowncastFrom for IterableType { + fn downcast_from<'a>(content: ::Content<'a, F>) -> Option> { match T::map_with::<'a, F, DynMapper>(content) { Ok(_) => panic!("DynMapper is expected to always short-circuit"), Err(dyn_leaf) => dyn_leaf, diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 30a6f169..fec4d660 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -36,9 +36,9 @@ pub(crate) trait MapIntoReturned: IsForm { // Clashes with other blanket impl it will replace! // // impl< -// X: FromValueContent<'static, TypeData = T, Ownership = F>, +// X: FromValueContent<'static, Type = T, Form = F>, // F: IsForm + MapFromArgument, -// T: MaybeMapFromType, +// T: DowncastFrom, // > IsArgument for X { // type ValueType = T; // const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; @@ -54,7 +54,7 @@ pub(crate) trait MapIntoReturned: IsForm { // impl< // X: IntoValueContent<'static, TypeData = T, Ownership = F>, // F: IsForm + MapIntoReturned, -// T: MapToType, +// T: UpcastTo, // > IsReturnable for X { // fn to_returned_value(self) -> ExecutionResult { // let type_mapped = self.into_actual() diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index a8e6d1a1..61623314 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -23,3 +23,13 @@ impl MapFromArgument for BeOwned { todo!() } } + +#[test] +fn can_resolve_owned() { + let owned_value: Owned = Owned::of(42u64); + let resolved = owned_value + .spanned(Span::call_site().span_range()) + .resolve_as::("My value") + .unwrap(); + assert_eq!(resolved, 42u64); +} diff --git a/src/expressions/concepts/type_impls.rs b/src/expressions/concepts/type_impls.rs index 1a49e396..ea26de3e 100644 --- a/src/expressions/concepts/type_impls.rs +++ b/src/expressions/concepts/type_impls.rs @@ -9,23 +9,23 @@ pub(crate) struct ValueType; // type ValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; // type ValueMut<'a> = Actual<'a, ValueType, BeAnyRefMut>; -impl MapToType for ValueType { - fn map_to_type<'a>(content: Self::Content<'a, F>) -> Self::Content<'a, F> { - content +impl IsType for ValueType { + type Content<'a, F: IsForm> = ValueContent<'a, F>; + + fn articled_type_name() -> &'static str { + "any value" } } -impl MaybeMapFromType for ValueType { - fn maybe_map_from_type<'a>(content: Self::Content<'a, F>) -> Option> { - Some(content) +impl UpcastTo for ValueType { + fn upcast_to<'a>(content: Self::Content<'a, F>) -> Self::Content<'a, F> { + content } } -impl IsType for ValueType { - type Content<'a, F: IsForm> = ValueContent<'a, F>; - - fn articled_type_name() -> &'static str { - "any value" +impl DowncastFrom for ValueType { + fn downcast_from<'a>(content: Self::Content<'a, F>) -> Option> { + Some(content) } } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 74b4485d..20869aa1 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -33,19 +33,19 @@ pub(crate) trait GeneralMapper { >; } -pub(crate) trait MapToType: IsType { - fn map_to_type<'a>(content: Self::Content<'a, F>) -> T::Content<'a, F>; +pub(crate) trait UpcastTo: IsType { + fn upcast_to<'a>(content: Self::Content<'a, F>) -> T::Content<'a, F>; } -pub(crate) trait MaybeMapFromType: IsType { - fn maybe_map_from_type<'a>(content: T::Content<'a, F>) -> Option>; +pub(crate) trait DowncastFrom: IsType { + fn downcast_from<'a>(content: T::Content<'a, F>) -> Option>; fn resolve<'a>( actual: Actual<'a, T, F>, span_range: SpanRange, resolution_target: &str, ) -> ExecutionResult> { - let content = match Self::maybe_map_from_type(actual.0) { + let content = match Self::downcast_from(actual.0) { Some(c) => c, None => { return span_range.value_err(format!( @@ -72,36 +72,36 @@ pub(crate) trait IsChildType: IsType { macro_rules! impl_ancestor_chain_conversions { ($child:ty => $parent:ty => [$($ancestor:ty),* $(,)?]) => { - impl MaybeMapFromType<$child, F> for $child + impl DowncastFrom<$child, F> for $child { - fn maybe_map_from_type<'a>( + fn downcast_from<'a>( content: <$child as IsType>::Content<'a, F>, ) -> Option<<$child as IsType>::Content<'a, F>> { Some(content) } } - impl MapToType<$child, F> for $child + impl UpcastTo<$child, F> for $child { - fn map_to_type<'a>( + fn upcast_to<'a>( content: <$child as IsType>::Content<'a, F>, ) -> <$child as IsType>::Content<'a, F> { content } } - impl MaybeMapFromType<$parent, F> for $child + impl DowncastFrom<$parent, F> for $child { - fn maybe_map_from_type<'a>( + fn downcast_from<'a>( content: <$parent as IsType>::Content<'a, F>, ) -> Option<<$child as IsType>::Content<'a, F>> { <$child as IsChildType>::from_parent(content) } } - impl MapToType<$parent, F> for $child + impl UpcastTo<$parent, F> for $child { - fn map_to_type<'a>( + fn upcast_to<'a>( content: <$child as IsType>::Content<'a, F>, ) -> <$parent as IsType>::Content<'a, F> { <$child as IsChildType>::into_parent(content) @@ -109,19 +109,19 @@ macro_rules! impl_ancestor_chain_conversions { } $( - impl MaybeMapFromType<$ancestor, F> for $child { - fn maybe_map_from_type<'a>( + impl DowncastFrom<$ancestor, F> for $child { + fn downcast_from<'a>( content: <$ancestor as IsType>::Content<'a, F>, ) -> Option<<$child as IsType>::Content<'a, F>> { - <$child as MaybeMapFromType<$parent, F>>::maybe_map_from_type(<$parent as MaybeMapFromType<$ancestor, F>>::maybe_map_from_type(content)?) + <$child as DowncastFrom<$parent, F>>::downcast_from(<$parent as DowncastFrom<$ancestor, F>>::downcast_from(content)?) } } - impl MapToType<$ancestor, F> for $child { - fn map_to_type<'a>( + impl UpcastTo<$ancestor, F> for $child { + fn upcast_to<'a>( content: <$child as IsType>::Content<'a, F>, ) -> <$ancestor as IsType>::Content<'a, F> { - <$parent as MapToType<$ancestor, F>>::map_to_type(<$child as MapToType<$parent, F>>::map_to_type(content)) + <$parent as UpcastTo<$ancestor, F>>::upcast_to(<$child as UpcastTo<$parent, F>>::upcast_to(content)) } } )* From 2f9b39393ef006a20abf37599dda46e0ca54c536 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 1 Jan 2026 11:35:35 +0100 Subject: [PATCH 03/58] fix: Fix MSRV --- src/expressions/concepts/forms/referenceable.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index 9406e41c..29ead2aa 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -29,8 +29,9 @@ impl MapFromArgument for BeReferenceable { impl<'a, T: IsHierarchyType> Actual<'a, T, BeOwned> { pub(crate) fn into_referencable(self) -> Actual<'a, T, BeReferenceable> { - let Ok(output) = self.map_with::(); - output + match self.map_with::() { + Ok(output) => output, + } } } From 6391cbf26575963be702307a0b079279c3a80c8d Mon Sep 17 00:00:00 2001 From: David Edey Date: Fri, 2 Jan 2026 03:30:39 +0100 Subject: [PATCH 04/58] refactor: GAT improvements --- plans/TODO.md | 3 + src/expressions/concepts/actual.rs | 57 ++-- src/expressions/concepts/dyn_impls.rs | 52 +--- src/expressions/concepts/form.rs | 38 ++- src/expressions/concepts/forms/any_mut.rs | 10 +- src/expressions/concepts/forms/any_ref.rs | 12 +- src/expressions/concepts/forms/argument.rs | 19 ++ src/expressions/concepts/forms/assignee.rs | 14 +- .../concepts/forms/copy_on_write.rs | 14 +- src/expressions/concepts/forms/late_bound.rs | 12 +- src/expressions/concepts/forms/mutable.rs | 12 +- src/expressions/concepts/forms/owned.rs | 14 +- .../concepts/forms/referenceable.rs | 33 +-- src/expressions/concepts/forms/shared.rs | 12 +- src/expressions/concepts/type_impls.rs | 188 ++---------- src/expressions/concepts/type_traits.rs | 280 +++++++++++++----- 16 files changed, 426 insertions(+), 344 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 17a4dfc6..d40725f2 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -204,6 +204,9 @@ First, read the @./2025-11-vision.md - [ ] Implement and roll-out GATs - [x] Initial shell implementation in `concepts` folder - [x] Improved error handling in the macro (e.g. required arg after optional; no matching strongly-typed signature) + - [x] Separate Hierarchical and DynCompatible Forms + - [x] Improved macro support + - [ ] Add ability to implement IsIterable - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): - [ ] Owned - [ ] Mutable, Owned => Mutable diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs index 7b524707..5789c649 100644 --- a/src/expressions/concepts/actual.rs +++ b/src/expressions/concepts/actual.rs @@ -1,44 +1,53 @@ use super::*; -pub(crate) struct Actual<'a, T: IsType, F: IsForm>(pub(crate) T::Content<'a, F>); +pub(crate) struct Actual<'a, T: IsType, F: IsFormOf>(pub(crate) F::Content<'a>); -impl<'a, T: IsType, F: IsForm> Actual<'a, T, F> { +impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> { #[inline] pub(crate) fn of(content: impl IntoValueContent<'a, Type = T, Form = F>) -> Self { Actual(content.into_content()) } #[inline] - pub(crate) fn map_type(self) -> Actual<'a, S, F> + pub(crate) fn upcast(self) -> Actual<'a, S, F> where + F: IsFormOf, T: UpcastTo, { Actual(T::upcast_to(self.0)) } #[inline] - pub(crate) fn map_type_maybe>(self) -> Option> { + pub(crate) fn downcast>(self) -> Option> + where + F: IsFormOf, + { Some(Actual(U::downcast_from(self.0)?)) } #[inline] - pub(crate) fn map_with>( + pub(crate) fn map_with>( self, ) -> Result, M::ShortCircuit<'a>> where - T: IsHierarchyType, + // TODO: See if we can move this bound lower down somehow? + // e.g. to IsHierarchicalType itself + // This may cause circular bound issues though, so let's do it in its own PR + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, { T::map_with::<'a, F, M>(self.0).map(|c| Actual(c)) } } -impl<'a, T: IsType, F: IsForm> Spanned> { +impl<'a, T: IsType, F: IsFormOf> Spanned> { #[inline] pub(crate) fn resolve_as>( self, description: &str, ) -> ExecutionResult where + F: IsFormOf<>::Type>, >::Type: DowncastFrom, { let Spanned(value, span_range) = self; @@ -47,39 +56,35 @@ impl<'a, T: IsType, F: IsForm> Spanned> { } } -impl<'a, T: IsType + UpcastTo, F: IsForm> Actual<'a, T, F> { +impl<'a, T: IsType + UpcastTo, F: IsFormOf + IsFormOf> + Actual<'a, T, F> +{ pub(crate) fn into_value(self) -> Actual<'a, ValueType, F> { - self.map_type() + self.upcast() } } -impl<'a, T: IsType, F: IsForm> Deref for Actual<'a, T, F> { - type Target = T::Content<'a, F>; +impl<'a, T: IsType, F: IsFormOf> Deref for Actual<'a, T, F> { + type Target = F::Content<'a>; fn deref(&self) -> &Self::Target { &self.0 } } -impl<'a, T: IsType, F: IsForm> DerefMut for Actual<'a, T, F> { +impl<'a, T: IsType, F: IsFormOf> DerefMut for Actual<'a, T, F> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -pub(crate) trait ContentMapper { - type TOut<'a>; - - fn map_leaf<'a, L: IsValueLeaf>(leaf: F::Leaf<'a, L>) -> Self::TOut<'a>; -} - pub(crate) trait IsValueContent<'a> { type Type: IsType; - type Form: IsForm; + type Form: IsFormOf; } pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { - fn into_content(self) -> ::Content<'a, Self::Form>; + fn into_content(self) -> >::Content<'a>; #[inline] fn into_actual(self) -> Actual<'a, Self::Type, Self::Form> @@ -91,7 +96,7 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { } pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { - fn from_content(content: ::Content<'a, Self::Form>) -> Self; + fn from_content(content: >::Content<'a>) -> Self; #[inline] fn from_actual(actual: Actual<'a, Self::Type, Self::Form>) -> Self @@ -102,19 +107,19 @@ pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { } } -impl<'a, T: IsType, F: IsForm> IsValueContent<'a> for Actual<'a, T, F> { +impl<'a, T: IsType, F: IsFormOf> IsValueContent<'a> for Actual<'a, T, F> { type Type = T; type Form = F; } -impl<'a, T: IsType, F: IsForm> FromValueContent<'a> for Actual<'a, T, F> { - fn from_content(content: ::Content<'a, F>) -> Self { +impl<'a, T: IsType, F: IsFormOf> FromValueContent<'a> for Actual<'a, T, F> { + fn from_content(content: >::Content<'a>) -> Self { Actual(content) } } -impl<'a, T: IsType, F: IsForm> IntoValueContent<'a> for Actual<'a, T, F> { - fn into_content(self) -> ::Content<'a, F> { +impl<'a, T: IsType, F: IsFormOf> IntoValueContent<'a> for Actual<'a, T, F> { + fn into_content(self) -> >::Content<'a> { self.0 } } diff --git a/src/expressions/concepts/dyn_impls.rs b/src/expressions/concepts/dyn_impls.rs index 6592a4b8..03084029 100644 --- a/src/expressions/concepts/dyn_impls.rs +++ b/src/expressions/concepts/dyn_impls.rs @@ -1,55 +1,11 @@ use super::*; -// NOTE: These should be moved out soon - -pub(crate) struct IterableType; - pub(crate) trait IsIterable: 'static { fn into_iterator(self: Box) -> ExecutionResult; fn len(&self, error_span_range: SpanRange) -> ExecutionResult; } -impl IsType for IterableType { - type Content<'a, F: IsForm> = F::DynLeaf<'a, dyn IsIterable>; - - fn articled_type_name() -> &'static str { - "an iterable (e.g. array, list, etc.)" - } -} - -impl IsDynType for IterableType { - type DynContent = dyn IsIterable; -} - -impl<'a> IsValueContent<'a> for dyn IsIterable { - type Type = IterableType; - type Form = BeOwned; -} - -impl IsDynLeaf for dyn IsIterable {} - -impl DowncastFrom for IterableType { - fn downcast_from<'a>(content: ::Content<'a, F>) -> Option> { - match T::map_with::<'a, F, DynMapper>(content) { - Ok(_) => panic!("DynMapper is expected to always short-circuit"), - Err(dyn_leaf) => dyn_leaf, - } - } -} - -pub(crate) struct DynMapper(std::marker::PhantomData); - -impl GeneralMapper for DynMapper { - type OutputForm = F; // Unused - type ShortCircuit<'a> = Option>; - - fn map_leaf<'a, L: IsValueLeaf, T: IsType>( - leaf: F::Leaf<'a, L>, - ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> -// where - // T: for<'l> IsType = F::Leaf<'l, L>>, - // T: for<'l> IsType = ::Leaf<'l, L>> - { - Err(F::leaf_to_dyn(leaf)) - } -} +define_dyn_type!( + dyn IsIterable => "an iterable (e.g. array, list, etc.)", + pub(crate) IterableType +); diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index fec4d660..4f4d6d7b 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -8,26 +8,56 @@ use super::*; /// - [BeMutable] representing [Mutable] references /// - [BeCopyOnWrite] representing [CopyOnWrite] values pub(crate) trait IsForm: Sized { + const ARGUMENT_OWNERSHIP: ArgumentOwnership; +} + +pub(crate) trait IsFormOf: IsForm { + type Content<'a>; +} + +pub(crate) trait IsFormOfForKind: IsForm { + type KindedContent<'a>; +} + +impl> IsFormOf for F { + type Content<'a> = F::KindedContent<'a>; +} + +pub(crate) trait IsHierarchicalForm: IsForm { /// The standard leaf for a hierachical type type Leaf<'a, T: IsValueLeaf>; +} + +impl IsFormOfForKind + for F +{ + type KindedContent<'a> = T::Content<'a, F>; +} + +pub(crate) trait IsDynCompatibleForm: IsForm { /// The container for a dyn Trait based type. /// The DynLeaf can be similar to the standard leaf, but must be /// able to support an unsized D. type DynLeaf<'a, D: 'static + ?Sized>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership; +} + +impl IsFormOfForKind for F { + type KindedContent<'a> = F::DynLeaf<'a, T::DynContent>; +} +pub(crate) trait IsDynMappableForm: IsHierarchicalForm + IsDynCompatibleForm { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option>; } -pub(crate) trait MapFromArgument: IsForm { +pub(crate) trait MapFromArgument: IsFormOf { fn from_argument_value( value: ArgumentValue, ) -> ExecutionResult>; } -pub(crate) trait MapIntoReturned: IsForm { +pub(crate) trait MapIntoReturned: IsFormOf { fn into_returned_value( value: Actual<'static, ValueType, Self>, ) -> ExecutionResult; @@ -58,7 +88,7 @@ pub(crate) trait MapIntoReturned: IsForm { // > IsReturnable for X { // fn to_returned_value(self) -> ExecutionResult { // let type_mapped = self.into_actual() -// .map_type::(); +// .upcast::(); // F::into_returned_value(type_mapped) // } // } diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index 3bfd49a2..a873e29f 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -2,11 +2,17 @@ use super::*; pub(crate) struct BeAnyMut; impl IsForm for BeAnyMut { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; +} +impl IsHierarchicalForm for BeAnyMut { type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyMut<'a, T>; - type DynLeaf<'a, T: 'static + ?Sized> = crate::internal_prelude::AnyMut<'a, T>; +} - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; +impl IsDynCompatibleForm for BeAnyMut { + type DynLeaf<'a, T: 'static + ?Sized> = crate::internal_prelude::AnyMut<'a, T>; +} +impl IsDynMappableForm for BeAnyMut { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> { diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 42bda9c1..5457c150 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -1,13 +1,21 @@ use super::*; -type AnyRef<'a, T> = Actual<'a, T, BeAnyRef>; +type QqqAnyRef<'a, T> = Actual<'a, T, BeAnyRef>; pub(crate) struct BeAnyRef; impl IsForm for BeAnyRef { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; +} + +impl IsHierarchicalForm for BeAnyRef { type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyRef<'a, T>; +} + +impl IsDynCompatibleForm for BeAnyRef { type DynLeaf<'a, T: 'static + ?Sized> = crate::internal_prelude::AnyRef<'a, T>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; +} +impl IsDynMappableForm for BeAnyRef { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized>( leaf: Self::Leaf<'a, T>, ) -> Option> { diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index 4563e55b..c2d072b1 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -1 +1,20 @@ use super::*; + +pub(crate) type QqqArgumentValue = Actual<'static, T, BeArgument>; + +pub(crate) struct BeArgument; +impl IsForm for BeArgument { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; +} + +impl IsHierarchicalForm for BeArgument { + type Leaf<'a, T: IsValueLeaf> = ArgumentContent; +} + +pub(crate) enum ArgumentContent { + Owned(T), + CopyOnWrite(CopyOnWriteContent), + Mutable(MutableSubRcRefCell), + Assignee(MutableSubRcRefCell), + Shared(SharedSubRcRefCell), +} diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index b2c296e9..ee2eb80b 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -1,14 +1,22 @@ use super::*; -type Assignee = Actual<'static, T, BeAssignee>; +type QqqAssignee = Actual<'static, T, BeAssignee>; pub(crate) struct BeAssignee; impl IsForm for BeAssignee { - type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; - type DynLeaf<'a, T: 'static + ?Sized> = MutableSubRcRefCell; const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Assignee { auto_create: false }; +} + +impl IsHierarchicalForm for BeAssignee { + type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; +} + +impl IsDynCompatibleForm for BeAssignee { + type DynLeaf<'a, T: 'static + ?Sized> = MutableSubRcRefCell; +} +impl IsDynMappableForm for BeAssignee { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> { diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index fa685651..137f3901 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -1,17 +1,25 @@ use super::*; -type CopyOnWrite = Actual<'static, T, BeCopyOnWrite>; +pub(crate) type QqqCopyOnWrite = Actual<'static, T, BeCopyOnWrite>; pub(crate) struct BeCopyOnWrite; impl IsForm for BeCopyOnWrite { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; +} + +impl IsHierarchicalForm for BeCopyOnWrite { type Leaf<'a, T: IsValueLeaf> = CopyOnWriteContent; +} + +impl IsDynCompatibleForm for BeCopyOnWrite { type DynLeaf<'a, T: 'static + ?Sized> = CopyOnWriteContent>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; +} +impl IsDynMappableForm for BeCopyOnWrite { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( _leaf: Self::Leaf<'a, T>, ) -> Option> { - // TODO: Add back once we add a map to LateBoundContent + // TODO: Add back once we add a map to CopyOnWriteContent todo!() } } diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index a88ba4d0..98f88701 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -10,14 +10,22 @@ use super::*; /// whether the method needs `x[a]` to be a shared reference, mutable reference or an owned value. /// /// So instead, we take the most powerful access we can have for `x[a]`, and convert it later. -type LateBound = Actual<'static, T, BeLateBound>; +pub(crate) type QqqLateBound = Actual<'static, T, BeLateBound>; pub(crate) struct BeLateBound; impl IsForm for BeLateBound { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; +} + +impl IsHierarchicalForm for BeLateBound { type Leaf<'a, T: IsValueLeaf> = LateBoundContent; +} + +impl IsDynCompatibleForm for BeLateBound { type DynLeaf<'a, T: 'static + ?Sized> = LateBoundContent>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; +} +impl IsDynMappableForm for BeLateBound { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( _leaf: Self::Leaf<'a, T>, ) -> Option> { diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index e7b4ae4a..cd07ba27 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -1,13 +1,21 @@ use super::*; -type Mutable = Actual<'static, T, BeMutable>; +pub(crate) type QqqMutable = Actual<'static, T, BeMutable>; pub(crate) struct BeMutable; impl IsForm for BeMutable { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; +} + +impl IsHierarchicalForm for BeMutable { type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; +} + +impl IsDynCompatibleForm for BeMutable { type DynLeaf<'a, T: 'static + ?Sized> = MutableSubRcRefCell; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; +} +impl IsDynMappableForm for BeMutable { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> { diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 61623314..78911495 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -1,13 +1,21 @@ use super::*; -type Owned = Actual<'static, T, BeOwned>; +pub(crate) type QqqOwned = Actual<'static, T, BeOwned>; pub(crate) struct BeOwned; impl IsForm for BeOwned { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; +} + +impl IsHierarchicalForm for BeOwned { type Leaf<'a, T: IsValueLeaf> = T; +} + +impl IsDynCompatibleForm for BeOwned { type DynLeaf<'a, T: 'static + ?Sized> = Box; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; +} +impl IsDynMappableForm for BeOwned { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> { @@ -26,7 +34,7 @@ impl MapFromArgument for BeOwned { #[test] fn can_resolve_owned() { - let owned_value: Owned = Owned::of(42u64); + let owned_value: QqqOwned = QqqOwned::of(42u64); let resolved = owned_value .spanned(Span::call_site().span_range()) .resolve_as::("My value") diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index 29ead2aa..8a517357 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -1,21 +1,19 @@ use super::*; -type Referenceable = Actual<'static, T, BeReferenceable>; +type QqqReferenceable = Actual<'static, T, BeReferenceable>; -// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. +/// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. +/// This is useful for the content of variables. +/// +/// Note that Referenceable form does not support dyn casting, because Rc> cannot be +/// directly cast to Rc>. pub(crate) struct BeReferenceable; impl IsForm for BeReferenceable { - type Leaf<'a, T: IsValueLeaf> = Rc>; - type DynLeaf<'a, T: 'static + ?Sized> = Rc>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; +} - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( - _leaf: Self::Leaf<'a, T>, - ) -> Option> { - // Can't map Rc> to Rc> directly - panic!("Casting to dyn is not supported for Referenceable form") - } +impl IsHierarchicalForm for BeReferenceable { + type Leaf<'a, T: IsValueLeaf> = Rc>; } impl MapFromArgument for BeReferenceable { @@ -27,27 +25,22 @@ impl MapFromArgument for BeReferenceable { } } -impl<'a, T: IsHierarchyType> Actual<'a, T, BeOwned> { +impl<'a, T: IsHierarchicalType> Actual<'a, T, BeOwned> { pub(crate) fn into_referencable(self) -> Actual<'a, T, BeReferenceable> { match self.map_with::() { Ok(output) => output, + Err(infallible) => match infallible {}, // Need to include because of MSRV } } } pub(crate) struct OwnedToReferencableMapper; -impl GeneralMapper for OwnedToReferencableMapper { +impl LeafMapper for OwnedToReferencableMapper { type OutputForm = BeReferenceable; type ShortCircuit<'a> = std::convert::Infallible; - fn map_leaf< - 'a, - L: IsValueLeaf, - T: for<'l> IsType = ::Leaf<'l, L>>, - >( - leaf: ::Leaf<'a, L>, - ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> { + fn map_leaf<'a, L: IsValueLeaf>(leaf: L) -> Result>, Self::ShortCircuit<'a>> { Ok(Rc::new(RefCell::new(leaf))) } } diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index a34cd5da..d5074466 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -1,13 +1,21 @@ use super::*; -type Shared = Actual<'static, T, BeShared>; +pub(crate) type QqqShared = Actual<'static, T, BeShared>; pub(crate) struct BeShared; impl IsForm for BeShared { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; +} + +impl IsHierarchicalForm for BeShared { type Leaf<'a, T: IsValueLeaf> = SharedSubRcRefCell; +} + +impl IsDynCompatibleForm for BeShared { type DynLeaf<'a, T: 'static + ?Sized> = SharedSubRcRefCell; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; +} +impl IsDynMappableForm for BeShared { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> { diff --git a/src/expressions/concepts/type_impls.rs b/src/expressions/concepts/type_impls.rs index ea26de3e..2dbef06a 100644 --- a/src/expressions/concepts/type_impls.rs +++ b/src/expressions/concepts/type_impls.rs @@ -2,171 +2,43 @@ use super::*; // NOTE: These should be moved out to the value definitions soon -pub(crate) struct ValueType; +pub(crate) type QqqValue = Actual<'static, ValueType, BeOwned>; +pub(crate) type QqqValueReferencable = Actual<'static, ValueType, BeReferenceable>; +pub(crate) type QqqValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; +pub(crate) type QqqValueMut<'a> = Actual<'a, ValueType, BeAnyMut>; -// type Value = Actual<'static, ValueType, BeOwned>; -// type ValueReferencable = Actual<'static, ValueType, BeReferencable>; -// type ValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; -// type ValueMut<'a> = Actual<'a, ValueType, BeAnyRefMut>; - -impl IsType for ValueType { - type Content<'a, F: IsForm> = ValueContent<'a, F>; - - fn articled_type_name() -> &'static str { - "any value" - } +define_parent_type! { + pub(crate) ValueType, + pub(crate) enum ValueContent { + Integer => IntegerType, + Object => ObjectType, + }, + "any value", } -impl UpcastTo for ValueType { - fn upcast_to<'a>(content: Self::Content<'a, F>) -> Self::Content<'a, F> { - content - } +define_parent_type! { + pub(crate) IntegerType => ValueType(ValueContent::Integer), + pub(crate) enum IntegerContent { + U32 => U32Type, + U64 => U64Type, + }, + "an integer", } -impl DowncastFrom for ValueType { - fn downcast_from<'a>(content: Self::Content<'a, F>) -> Option> { - Some(content) - } +define_leaf_type! { + pub(crate) U32Type => IntegerType(IntegerContent::U32) => ValueType, + u32, + "a u32", } -impl IsHierarchyType for ValueType { - fn map_with<'a, F: IsForm, M: GeneralMapper>( - content: Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>> { - match content { - ValueContent::Integer(x) => Ok(ValueContent::Integer(x.map_with::()?)), - ValueContent::Object(x) => Ok(ValueContent::Object(x.map_with::()?)), - } - } -} - -pub(crate) enum ValueContent<'a, F: IsForm> { - Integer(Actual<'a, IntegerType, F>), - Object(Actual<'a, ObjectType, F>), - // ... -} - -pub(crate) enum IntegerContent<'a, F: IsForm> { - U32(Actual<'a, U32Type, F>), - U64(Actual<'a, U64Type, F>), - // ... -} - -pub(crate) struct IntegerType; - -impl IsType for IntegerType { - type Content<'a, F: IsForm> = IntegerContent<'a, F>; - - fn articled_type_name() -> &'static str { - "an integer" - } -} - -impl IsHierarchyType for IntegerType { - fn map_with<'a, F: IsForm, M: GeneralMapper>( - content: Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>> { - match content { - IntegerContent::U32(x) => Ok(IntegerContent::U32(x.map_with::()?)), - IntegerContent::U64(x) => Ok(IntegerContent::U64(x.map_with::()?)), - } - } -} - -impl_ancestor_chain_conversions!( - IntegerType => ValueType => [] -); - -impl IsChildType for IntegerType { - type ParentType = ValueType; - - fn into_parent<'a, F: IsForm>( - content: Self::Content<'a, F>, - ) -> ::Content<'a, F> { - ValueContent::Integer(Actual(content)) - } - - fn from_parent<'a, F: IsForm>( - content: ::Content<'a, F>, - ) -> Option> { - match content { - ValueContent::Integer(i) => Some(i.0), - _ => None, - } - } +define_leaf_type! { + pub(crate) U64Type => IntegerType(IntegerContent::U64) => ValueType, + u64, + "a u64", } -define_leaf_type!(u32: pub(crate) U32Type "a u32"); - -impl_ancestor_chain_conversions!( - U32Type => IntegerType => [ValueType] -); - -impl IsChildType for U32Type { - type ParentType = IntegerType; - - fn into_parent<'a, F: IsForm>( - content: Self::Content<'a, F>, - ) -> ::Content<'a, F> { - IntegerContent::U32(Actual(content)) - } - - fn from_parent<'a, F: IsForm>( - content: ::Content<'a, F>, - ) -> Option> { - match content { - IntegerContent::U32(i) => Some(i.0), - _ => None, - } - } -} - -define_leaf_type!(u64: pub(crate) U64Type "a u64"); - -impl_ancestor_chain_conversions!( - U64Type => IntegerType => [ValueType] -); - -impl IsChildType for U64Type { - type ParentType = IntegerType; - - fn into_parent<'a, F: IsForm>( - content: Self::Content<'a, F>, - ) -> ::Content<'a, F> { - IntegerContent::U64(Actual(content)) - } - - fn from_parent<'a, F: IsForm>( - content: ::Content<'a, F>, - ) -> Option> { - match content { - IntegerContent::U64(i) => Some(i.0), - _ => None, - } - } -} - -define_leaf_type!(ObjectValue: pub(crate) ObjectType "an object"); - -impl_ancestor_chain_conversions!( - ObjectType => ValueType => [] -); - -impl IsChildType for ObjectType { - type ParentType = ValueType; - - fn into_parent<'a, F: IsForm>( - content: Self::Content<'a, F>, - ) -> ::Content<'a, F> { - ValueContent::Object(Actual(content)) - } - - fn from_parent<'a, F: IsForm>( - content: ::Content<'a, F>, - ) -> Option> { - match content { - ValueContent::Object(o) => Some(o.0), - _ => None, - } - } +define_leaf_type! { + pub(crate) ObjectType => ValueType(ValueContent::Object), + ObjectValue, + "an object", } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 20869aa1..e5b45aa0 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -1,44 +1,55 @@ use super::*; +pub(crate) trait TypeVariant {} + +pub(crate) struct HierarchicalTypeVariant; +impl TypeVariant for HierarchicalTypeVariant {} + +pub(crate) struct DynTypeVariant; +impl TypeVariant for DynTypeVariant {} + pub(crate) trait IsType: Sized { - type Content<'a, F: IsForm>; + type Variant: TypeVariant; fn articled_type_name() -> &'static str; } -pub(crate) trait IsHierarchyType: IsType { - fn map_with<'a, F: IsForm, M: GeneralMapper>( - content: Self::Content<'a, F>, +pub(crate) trait IsHierarchicalType: IsType { + // >::Content<'a>> := Self::Content<'a, F> + type Content<'a, F: IsHierarchicalForm>; + + fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( + structure: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; } -pub(crate) trait IsDynType: IsType -where - DynMapper: GeneralMapper, +pub(crate) trait IsDynType: IsType +// TODO: Add this assertion somewhere to check that all DynTypes can be downcasted into +// where +// DynMapper: LeafMapper, { - type DynContent: ?Sized; + type DynContent: ?Sized + 'static; } -pub(crate) trait GeneralMapper { - type OutputForm: IsForm; +pub(crate) trait LeafMapper { + type OutputForm: IsHierarchicalForm; type ShortCircuit<'a>; - fn map_leaf<'a, L: IsValueLeaf, T>( + fn map_leaf<'a, L: IsValueLeaf>( leaf: F::Leaf<'a, L>, - ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> - where - T: for<'l> IsType = F::Leaf<'l, L>>, - T: for<'l> IsType< - Content<'l, Self::OutputForm> = ::Leaf<'l, L>, - >; + ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>>; } -pub(crate) trait UpcastTo: IsType { - fn upcast_to<'a>(content: Self::Content<'a, F>) -> T::Content<'a, F>; +pub(crate) trait UpcastTo + IsFormOf>: IsType { + fn upcast_to<'a>( + content: >::Content<'a>, + ) -> >::Content<'a>; } -pub(crate) trait DowncastFrom: IsType { - fn downcast_from<'a>(content: T::Content<'a, F>) -> Option>; +pub(crate) trait DowncastFrom + IsFormOf>: IsType { + fn downcast_from<'a>( + content: >::Content<'a>, + ) -> Option<>::Content<'a>>; fn resolve<'a>( actual: Actual<'a, T, F>, @@ -60,71 +71,94 @@ pub(crate) trait DowncastFrom: IsType { } } -pub(crate) trait IsChildType: IsType { - type ParentType: IsType; - fn into_parent<'a, F: IsForm>( +pub(crate) trait IsChildType: IsHierarchicalType { + type ParentType: IsHierarchicalType; + + fn into_parent<'a, F: IsHierarchicalForm>( content: Self::Content<'a, F>, - ) -> ::Content<'a, F>; - fn from_parent<'a, F: IsForm>( - content: ::Content<'a, F>, + ) -> ::Content<'a, F>; + + fn from_parent<'a, F: IsHierarchicalForm>( + content: ::Content<'a, F>, ) -> Option>; } macro_rules! impl_ancestor_chain_conversions { - ($child:ty => $parent:ty => [$($ancestor:ty),* $(,)?]) => { - impl DowncastFrom<$child, F> for $child + ($child:ty $(=> $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?) => { + impl DowncastFrom<$child, F> for $child { fn downcast_from<'a>( - content: <$child as IsType>::Content<'a, F>, - ) -> Option<<$child as IsType>::Content<'a, F>> { + content: >::Content<'a>, + ) -> Option<>::Content<'a>> { Some(content) } } - impl UpcastTo<$child, F> for $child + impl UpcastTo<$child, F> for $child { fn upcast_to<'a>( - content: <$child as IsType>::Content<'a, F>, - ) -> <$child as IsType>::Content<'a, F> { + content: >::Content<'a>, + ) -> >::Content<'a> { content } } - impl DowncastFrom<$parent, F> for $child - { - fn downcast_from<'a>( - content: <$parent as IsType>::Content<'a, F>, - ) -> Option<<$child as IsType>::Content<'a, F>> { - <$child as IsChildType>::from_parent(content) - } - } + $( + impl IsChildType for $child { + type ParentType = $parent; - impl UpcastTo<$parent, F> for $child - { - fn upcast_to<'a>( - content: <$child as IsType>::Content<'a, F>, - ) -> <$parent as IsType>::Content<'a, F> { - <$child as IsChildType>::into_parent(content) + fn into_parent<'a, F: IsHierarchicalForm>( + content: Self::Content<'a, F>, + ) -> ::Content<'a, F> { + $parent_content::$parent_variant(Actual(content)) + } + + fn from_parent<'a, F: IsHierarchicalForm>( + content: ::Content<'a, F>, + ) -> Option> { + match content { + $parent_content::$parent_variant(i) => Some(i.0), + _ => None, + } + } } - } - $( - impl DowncastFrom<$ancestor, F> for $child { + impl DowncastFrom<$parent, F> for $child + { fn downcast_from<'a>( - content: <$ancestor as IsType>::Content<'a, F>, - ) -> Option<<$child as IsType>::Content<'a, F>> { - <$child as DowncastFrom<$parent, F>>::downcast_from(<$parent as DowncastFrom<$ancestor, F>>::downcast_from(content)?) + content: >::Content<'a>, + ) -> Option<>::Content<'a>> { + <$child as IsChildType>::from_parent(content) } } - impl UpcastTo<$ancestor, F> for $child { + impl UpcastTo<$parent, F> for $child + { fn upcast_to<'a>( - content: <$child as IsType>::Content<'a, F>, - ) -> <$ancestor as IsType>::Content<'a, F> { - <$parent as UpcastTo<$ancestor, F>>::upcast_to(<$child as UpcastTo<$parent, F>>::upcast_to(content)) + content: >::Content<'a>, + ) -> >::Content<'a> { + <$child as IsChildType>::into_parent(content) } } - )* + + $( + impl DowncastFrom<$ancestor, F> for $child { + fn downcast_from<'a>( + content: >::Content<'a>, + ) -> Option<>::Content<'a>> { + <$child as DowncastFrom<$parent, F>>::downcast_from(<$parent as DowncastFrom<$ancestor, F>>::downcast_from(content)?) + } + } + + impl UpcastTo<$ancestor, F> for $child { + fn upcast_to<'a>( + content: >::Content<'a>, + ) -> >::Content<'a> { + <$parent as UpcastTo<$ancestor, F>>::upcast_to(<$child as UpcastTo<$parent, F>>::upcast_to(content)) + } + } + )* + )? }; } @@ -137,7 +171,7 @@ pub(crate) trait IsValueLeaf: pub(crate) trait IsDynLeaf: 'static + IsValueContent<'static> where - DynMapper: GeneralMapper, + DynMapper: LeafMapper, Self::Type: IsDynType, { } @@ -154,28 +188,76 @@ pub(crate) trait CastDyn { } } +macro_rules! define_parent_type { + ( + $type_def_vis:vis $type_def:ident $(=> $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?, + $content_vis:vis enum $content:ident { + $($variant:ident => $variant_type:ty,)* + }, + $articled_type_name:literal, + ) => { + $type_def_vis struct $type_def; + + impl IsType for $type_def { + type Variant = HierarchicalTypeVariant; + + fn articled_type_name() -> &'static str { + $articled_type_name + } + } + + impl IsHierarchicalType for $type_def { + type Content<'a, F: IsHierarchicalForm> = $content<'a, F>; + + fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( + content: Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>> { + Ok(match content { + $( $content::$variant(x) => $content::$variant(x.map_with::()?), )* + }) + } + } + + $content_vis enum $content<'a, F: IsHierarchicalForm> { + $( $variant(Actual<'a, $variant_type, F>), )* + } + + impl_ancestor_chain_conversions!( + $type_def $(=> $parent($parent_content :: $parent_variant) $(=> $ancestor)*)? + ); + }; +} + +pub(crate) use define_parent_type; + macro_rules! define_leaf_type { - ($leaf_type:ty : $type_data_vis:vis $leaf_type_def:ident $articled_type_name:literal) => { - $type_data_vis struct $leaf_type_def; + ( + $type_def_vis:vis $type_def:ident => $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*, + $leaf_type:ty, + $articled_type_name:literal, + ) => { + $type_def_vis struct $type_def; - impl IsType for $leaf_type_def { - type Content<'a, F: IsForm> = F::Leaf<'a, $leaf_type>; + impl IsType for $type_def { + type Variant = HierarchicalTypeVariant; fn articled_type_name() -> &'static str { $articled_type_name } } - impl IsHierarchyType for $leaf_type_def { - fn map_with<'a, F: IsForm, M: GeneralMapper>( + impl IsHierarchicalType for $type_def { + type Content<'a, F: IsHierarchicalForm> = F::Leaf<'a, $leaf_type>; + + fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( content: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { - M::map_leaf::<$leaf_type, Self>(content) + M::map_leaf::<$leaf_type>(content) } } impl<'a> IsValueContent<'a> for $leaf_type { - type Type = $leaf_type_def; + type Type = $type_def; type Form = BeOwned; } @@ -193,8 +275,68 @@ macro_rules! define_leaf_type { impl IsValueLeaf for $leaf_type {} impl CastDyn for $leaf_type {} - }; + impl_ancestor_chain_conversions!( + $type_def => $parent($parent_content :: $parent_variant) $(=> $ancestor)* + ); + }; } pub(crate) use define_leaf_type; + +pub(crate) struct DynMapper(std::marker::PhantomData); + +macro_rules! define_dyn_type { + ( + $dyn_type:ty => $articled_type_name:literal, + $type_def_vis:vis $type_def:ident + ) => { + $type_def_vis struct $type_def; + + impl IsType for $type_def { + type Variant = DynTypeVariant; + + fn articled_type_name() -> &'static str { + $articled_type_name + } + } + + impl IsDynType for $type_def { + type DynContent = $dyn_type; + } + + impl<'a> IsValueContent<'a> for $dyn_type { + type Type = $type_def; + type Form = BeOwned; + } + + impl IsDynLeaf for $dyn_type {} + + impl + IsFormOf<$type_def> + IsDynMappableForm> DowncastFrom for $type_def + where + for<'a> T: IsHierarchicalType = >::Content<'a>>, + for<'a> F: IsDynCompatibleForm = >::Content<'a>>, + { + fn downcast_from<'a>(content: >::Content<'a>) -> Option<>::Content<'a>> { + match T::map_with::<'a, F, DynMapper<$dyn_type>>(content) { + Ok(_) => panic!("DynMapper is expected to always short-circuit"), + Err(dyn_leaf) => dyn_leaf, + } + } + } + + impl LeafMapper for DynMapper<$dyn_type> { + type OutputForm = BeOwned; // Unused + type ShortCircuit<'a> = Option>; + + fn map_leaf<'a, L: IsValueLeaf>( + leaf: F::Leaf<'a, L>, + ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> + { + Err(F::leaf_to_dyn(leaf)) + } + } + }; +} + +pub(crate) use define_dyn_type; From fc9ff9b82b6b31e33e75064862624671acb4f135 Mon Sep 17 00:00:00 2001 From: David Edey Date: Fri, 2 Jan 2026 19:37:53 +0100 Subject: [PATCH 05/58] refactor: Further concept improvements --- plans/TODO.md | 5 + src/expressions/concepts/type_impls.rs | 6 +- src/expressions/concepts/type_traits.rs | 41 +- src/expressions/evaluation/value_frames.rs | 8 +- src/expressions/operations.rs | 24 +- src/expressions/type_resolution/arguments.rs | 2 +- .../type_resolution/value_kinds.rs | 369 +++++++++--------- src/expressions/values/range.rs | 49 +-- src/expressions/values/value.rs | 24 +- 9 files changed, 276 insertions(+), 252 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index d40725f2..d0dcc2c9 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -206,7 +206,9 @@ First, read the @./2025-11-vision.md - [x] Improved error handling in the macro (e.g. required arg after optional; no matching strongly-typed signature) - [x] Separate Hierarchical and DynCompatible Forms - [x] Improved macro support + - [ ] Add source type name to type macro/s - [ ] Add ability to implement IsIterable + - [ ] `CastTarget` simply wraps `TypeKind` - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): - [ ] Owned - [ ] Mutable, Owned => Mutable @@ -216,8 +218,11 @@ First, read the @./2025-11-vision.md - [ ] LateBound, Tons of conversions into it - [ ] Argument, and `ArgumentOwnership` driven conversions into it - [ ] Complete value definitions + - [ ] Strip wrapper types like `StreamValue` - can just use `OutputStream` as content - [ ] Replace `Owned`, `Shared` etc as type references to `Actual<..>` - [ ] Remove old definitions + - [ ] Generate test over all value kinds which checks for: + - [ ] source type has no spaces and is lower case, and is invertible ## Methods and closures diff --git a/src/expressions/concepts/type_impls.rs b/src/expressions/concepts/type_impls.rs index 2dbef06a..fa82d2fc 100644 --- a/src/expressions/concepts/type_impls.rs +++ b/src/expressions/concepts/type_impls.rs @@ -26,19 +26,19 @@ define_parent_type! { } define_leaf_type! { - pub(crate) U32Type => IntegerType(IntegerContent::U32) => ValueType, + pub(crate) U32Type => IntegerType(IntegerContent::U32, IntegerKind::U32) => ValueType, u32, "a u32", } define_leaf_type! { - pub(crate) U64Type => IntegerType(IntegerContent::U64) => ValueType, + pub(crate) U64Type => IntegerType(IntegerContent::U64, IntegerKind::U64) => ValueType, u64, "a u64", } define_leaf_type! { - pub(crate) ObjectType => ValueType(ValueContent::Object), + pub(crate) ObjectType => ValueType(ValueContent::Object, ValueKind::Object), ObjectValue, "an object", } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index e5b45aa0..ceca846e 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -23,11 +23,7 @@ pub(crate) trait IsHierarchicalType: IsType { ) -> Result, M::ShortCircuit<'a>>; } -pub(crate) trait IsDynType: IsType -// TODO: Add this assertion somewhere to check that all DynTypes can be downcasted into -// where -// DynMapper: LeafMapper, -{ +pub(crate) trait IsDynType: IsType { type DynContent: ?Sized + 'static; } @@ -188,6 +184,33 @@ pub(crate) trait CastDyn { } } +pub(crate) trait HasLeafKind { + type LeafKindType: IsSpecificLeafKind; + + const KIND: Self::LeafKindType; + + fn kind(&self) -> Self::LeafKindType { + Self::KIND + } + + fn value_kind(&self) -> ValueKind { + self.kind().into() + } + + fn articled_kind(&self) -> &'static str { + self.kind().articled_display_name() + } +} + +impl> HasLeafKind for T +where + T::Type: HasLeafKind, +{ + type LeafKindType = ::LeafKindType; + + const KIND: Self::LeafKindType = T::Type::KIND; +} + macro_rules! define_parent_type { ( $type_def_vis:vis $type_def:ident $(=> $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?, @@ -232,7 +255,7 @@ pub(crate) use define_parent_type; macro_rules! define_leaf_type { ( - $type_def_vis:vis $type_def:ident => $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*, + $type_def_vis:vis $type_def:ident => $parent:ident($parent_content:ident :: $parent_variant:ident, $leaf_kind_type:ident :: $leaf_kind_variant:ident) $(=> $ancestor:ty)*, $leaf_type:ty, $articled_type_name:literal, ) => { @@ -246,6 +269,12 @@ macro_rules! define_leaf_type { } } + impl HasLeafKind for $type_def { + type LeafKindType = $leaf_kind_type; + + const KIND: Self::LeafKindType = $leaf_kind_type::$leaf_kind_variant; + } + impl IsHierarchicalType for $type_def { type Content<'a, F: IsHierarchicalForm> = F::Leaf<'a, $leaf_type>; diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 09cb43df..0a3ae5dd 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -802,9 +802,9 @@ impl EvaluationFrame for UnaryOperationBuilder { return context.return_returned_value(result); } self.operation.type_err(format!( - "The {} operator is not supported for {} values", + "The {} operator is not supported for {}", self.operation.symbolic_description(), - operand.value_type(), + operand.articled_kind(), )) } } @@ -888,7 +888,7 @@ impl EvaluationFrame for BinaryOperationBuilder { return self.operation.type_err(format!( "The {} operator is not supported for {} operand", self.operation.symbolic_description(), - left.articled_value_type(), + left.articled_kind(), )); } } @@ -1263,7 +1263,7 @@ impl EvaluationFrame for MethodCallBuilder { return self.method.method.type_err(format!( "The method {} does not exist on {}", self.method.method, - caller.articled_value_type(), + caller.articled_kind(), )) } }; diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index 7927ce0c..7ce286e7 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -59,7 +59,7 @@ impl UnaryOperation { as_token: Token![as], target_ident: Ident, ) -> ParseResult { - let target = Type::from_ident(&target_ident)?; + let target = TypeIdent::from_ident(&target_ident)?; let target = CastTarget::from_source_type(target).ok_or_else(|| { target_ident.parse_error("This type is not supported in cast expressions") })?; @@ -91,7 +91,7 @@ impl UnaryOperation { self.type_error(format!( "The {} operator is not supported for {} operand", self.symbolic_description(), - input.articled_value_type(), + input.articled_kind(), )) })?; let input = method @@ -112,16 +112,16 @@ pub(crate) enum CastTarget { } impl CastTarget { - fn from_source_type(s: Type) -> Option { + fn from_source_type(s: TypeIdent) -> Option { Some(match s.kind { - TypeKind::Integer => CastTarget::Integer(IntegerKind::Untyped), - TypeKind::SpecificInteger(kind) => CastTarget::Integer(kind), - TypeKind::Float => CastTarget::Float(FloatKind::Untyped), - TypeKind::SpecificFloat(kind) => CastTarget::Float(kind), - TypeKind::Boolean => CastTarget::Boolean, - TypeKind::String => CastTarget::String, - TypeKind::Char => CastTarget::Char, - TypeKind::Stream => CastTarget::Stream, + TypeKind::Parent(ParentTypeKind::Integer) => CastTarget::Integer(IntegerKind::Untyped), + TypeKind::Leaf(ValueKind::Integer(kind)) => CastTarget::Integer(kind), + TypeKind::Parent(ParentTypeKind::Float) => CastTarget::Float(FloatKind::Untyped), + TypeKind::Leaf(ValueKind::Float(kind)) => CastTarget::Float(kind), + TypeKind::Leaf(ValueKind::Boolean) => CastTarget::Boolean, + TypeKind::Leaf(ValueKind::String) => CastTarget::String, + TypeKind::Leaf(ValueKind::Char) => CastTarget::Char, + TypeKind::Leaf(ValueKind::Stream) => CastTarget::Stream, _ => return None, }) } @@ -336,7 +336,7 @@ impl BinaryOperation { None => self.type_err(format!( "The {} operator is not supported for {} operand", self.symbolic_description(), - left.articled_value_type(), + left.articled_kind(), )), } } diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index 0e836ecb..ed832ad8 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -35,7 +35,7 @@ impl<'a> ResolutionContext<'a> { "{} is expected to be {}, but it is {}", self.resolution_target, articled_expected_value_kind, - value.articled_value_type() + value.articled_kind() )) } } diff --git a/src/expressions/type_resolution/value_kinds.rs b/src/expressions/type_resolution/value_kinds.rs index 77f2ecc9..c59dbe56 100644 --- a/src/expressions/type_resolution/value_kinds.rs +++ b/src/expressions/type_resolution/value_kinds.rs @@ -2,15 +2,13 @@ use super::*; /// A trait for specific value kinds that can provide a display name. /// This is implemented by `ValueKind`, `IntegerKind`, `FloatKind`, etc. -pub(crate) trait IsSpecificValueKind: Copy + Into { - fn display_name(&self) -> &'static str; - +pub(crate) trait IsSpecificLeafKind: Copy + Into { fn articled_display_name(&self) -> &'static str; } /// A trait for types that have a value kind. pub(crate) trait HasValueKind { - type SpecificKind: IsSpecificValueKind; + type SpecificKind: IsSpecificLeafKind; fn kind(&self) -> Self::SpecificKind; @@ -18,11 +16,7 @@ pub(crate) trait HasValueKind { self.kind().into() } - fn value_type(&self) -> &'static str { - self.kind().display_name() - } - - fn articled_value_type(&self) -> &'static str { + fn articled_kind(&self) -> &'static str { self.kind().articled_display_name() } } @@ -60,25 +54,84 @@ pub(crate) enum ValueKind { Parser, } -impl IsSpecificValueKind for ValueKind { - fn display_name(&self) -> &'static str { +impl ValueKind { + pub(crate) fn from_source_name(name: &str) -> Option { + Some(match name { + "none" => ValueKind::None, + "untyped_int" => ValueKind::Integer(IntegerKind::Untyped), + "i8" => ValueKind::Integer(IntegerKind::I8), + "i16" => ValueKind::Integer(IntegerKind::I16), + "i32" => ValueKind::Integer(IntegerKind::I32), + "i64" => ValueKind::Integer(IntegerKind::I64), + "i128" => ValueKind::Integer(IntegerKind::I128), + "isize" => ValueKind::Integer(IntegerKind::Isize), + "u8" => ValueKind::Integer(IntegerKind::U8), + "u16" => ValueKind::Integer(IntegerKind::U16), + "u32" => ValueKind::Integer(IntegerKind::U32), + "u64" => ValueKind::Integer(IntegerKind::U64), + "u128" => ValueKind::Integer(IntegerKind::U128), + "usize" => ValueKind::Integer(IntegerKind::Usize), + "untyped_float" => ValueKind::Float(FloatKind::Untyped), + "f32" => ValueKind::Float(FloatKind::F32), + "f64" => ValueKind::Float(FloatKind::F64), + "bool" => ValueKind::Boolean, + "string" => ValueKind::String, + "unsupported_literal" => ValueKind::UnsupportedLiteral, + "char" => ValueKind::Char, + "array" => ValueKind::Array, + "object" => ValueKind::Object, + "stream" => ValueKind::Stream, + "range_from_to" => ValueKind::Range(RangeKind::FromTo), + "range_from" => ValueKind::Range(RangeKind::From), + "range_to" => ValueKind::Range(RangeKind::To), + "range_full" => ValueKind::Range(RangeKind::Full), + "range_from_to_inclusive" => ValueKind::Range(RangeKind::FromToInclusive), + "range_to_inclusive" => ValueKind::Range(RangeKind::ToInclusive), + "iterator" => ValueKind::Iterator, + "parser" => ValueKind::Parser, + _ => return None, + }) + } + + pub(crate) fn source_name(&self) -> &'static str { match self { - ValueKind::None => "None", - ValueKind::Integer(kind) => kind.display_name(), - ValueKind::Float(kind) => kind.display_name(), + ValueKind::None => "none", + ValueKind::Integer(IntegerKind::Untyped) => "untyped_int", + ValueKind::Integer(IntegerKind::I8) => "i8", + ValueKind::Integer(IntegerKind::I16) => "i16", + ValueKind::Integer(IntegerKind::I32) => "i32", + ValueKind::Integer(IntegerKind::I64) => "i64", + ValueKind::Integer(IntegerKind::I128) => "i128", + ValueKind::Integer(IntegerKind::Isize) => "isize", + ValueKind::Integer(IntegerKind::U8) => "u8", + ValueKind::Integer(IntegerKind::U16) => "u16", + ValueKind::Integer(IntegerKind::U32) => "u32", + ValueKind::Integer(IntegerKind::U64) => "u64", + ValueKind::Integer(IntegerKind::U128) => "u128", + ValueKind::Integer(IntegerKind::Usize) => "usize", + ValueKind::Float(FloatKind::Untyped) => "untyped_float", + ValueKind::Float(FloatKind::F32) => "f32", + ValueKind::Float(FloatKind::F64) => "f64", ValueKind::Boolean => "bool", ValueKind::String => "string", ValueKind::Char => "char", - ValueKind::UnsupportedLiteral => "unsupported literal", + ValueKind::UnsupportedLiteral => "unsupported_literal", ValueKind::Array => "array", ValueKind::Object => "object", ValueKind::Stream => "stream", - ValueKind::Range(kind) => kind.display_name(), + ValueKind::Range(RangeKind::FromTo) => "range_from_to", + ValueKind::Range(RangeKind::From) => "range_from", + ValueKind::Range(RangeKind::To) => "range_to", + ValueKind::Range(RangeKind::Full) => "range_full", + ValueKind::Range(RangeKind::FromToInclusive) => "range_from_to_inclusive", + ValueKind::Range(RangeKind::ToInclusive) => "range_to_inclusive", ValueKind::Iterator => "iterator", ValueKind::Parser => "parser", } } +} +impl IsSpecificLeafKind for ValueKind { fn articled_display_name(&self) -> &'static str { match self { // Instead of saying "expected a none value", we can say "expected None" @@ -99,35 +152,22 @@ impl IsSpecificValueKind for ValueKind { } } -static NONE: NoneTypeData = NoneTypeData; -static BOOLEAN: BooleanTypeData = BooleanTypeData; -static STRING: StringTypeData = StringTypeData; -static CHAR: CharTypeData = CharTypeData; -static UNSUPPORTED_LITERAL: UnsupportedLiteralTypeData = UnsupportedLiteralTypeData; -static ARRAY: ArrayTypeData = ArrayTypeData; -static OBJECT: ObjectTypeData = ObjectTypeData; -static STREAM: StreamTypeData = StreamTypeData; -static RANGE: RangeTypeData = RangeTypeData; -static ITERABLE: IterableTypeData = IterableTypeData; -static ITERATOR: IteratorTypeData = IteratorTypeData; -static PARSER: ParserTypeData = ParserTypeData; - impl ValueKind { fn method_resolver(&self) -> &'static dyn MethodResolver { match self { - ValueKind::None => &NONE, + ValueKind::None => &NoneTypeData, ValueKind::Integer(kind) => kind.method_resolver(), ValueKind::Float(kind) => kind.method_resolver(), - ValueKind::Boolean => &BOOLEAN, - ValueKind::String => &STRING, - ValueKind::Char => &CHAR, - ValueKind::UnsupportedLiteral => &UNSUPPORTED_LITERAL, - ValueKind::Array => &ARRAY, - ValueKind::Object => &OBJECT, - ValueKind::Stream => &STREAM, - ValueKind::Range(_) => &RANGE, - ValueKind::Iterator => &ITERATOR, - ValueKind::Parser => &PARSER, + ValueKind::Boolean => &BooleanTypeData, + ValueKind::String => &StringTypeData, + ValueKind::Char => &CharTypeData, + ValueKind::UnsupportedLiteral => &UnsupportedLiteralTypeData, + ValueKind::Array => &ArrayTypeData, + ValueKind::Object => &ObjectTypeData, + ValueKind::Stream => &StreamTypeData, + ValueKind::Range(_) => &RangeTypeData, + ValueKind::Iterator => &IteratorTypeData, + ValueKind::Parser => &ParserTypeData, } } @@ -200,25 +240,7 @@ pub(crate) enum IntegerKind { Usize, } -impl IsSpecificValueKind for IntegerKind { - fn display_name(&self) -> &'static str { - match self { - IntegerKind::Untyped => "untyped integer", - IntegerKind::I8 => "i8", - IntegerKind::I16 => "i16", - IntegerKind::I32 => "i32", - IntegerKind::I64 => "i64", - IntegerKind::I128 => "i128", - IntegerKind::Isize => "isize", - IntegerKind::U8 => "u8", - IntegerKind::U16 => "u16", - IntegerKind::U32 => "u32", - IntegerKind::U64 => "u64", - IntegerKind::U128 => "u128", - IntegerKind::Usize => "usize", - } - } - +impl IsSpecificLeafKind for IntegerKind { fn articled_display_name(&self) -> &'static str { match self { IntegerKind::Untyped => "an untyped integer", @@ -244,37 +266,22 @@ impl From for ValueKind { } } -static INTEGER: IntegerTypeData = IntegerTypeData; -static UNTYPED_INTEGER: UntypedIntegerTypeData = UntypedIntegerTypeData; -static I8: I8TypeData = I8TypeData; -static I16: I16TypeData = I16TypeData; -static I32: I32TypeData = I32TypeData; -static I64: I64TypeData = I64TypeData; -static I128: I128TypeData = I128TypeData; -static ISIZE: IsizeTypeData = IsizeTypeData; -static U8: U8TypeData = U8TypeData; -static U16: U16TypeData = U16TypeData; -static U32: U32TypeData = U32TypeData; -static U64: U64TypeData = U64TypeData; -static U128: U128TypeData = U128TypeData; -static USIZE: UsizeTypeData = UsizeTypeData; - impl IntegerKind { pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { match self { - IntegerKind::Untyped => &UNTYPED_INTEGER, - IntegerKind::I8 => &I8, - IntegerKind::I16 => &I16, - IntegerKind::I32 => &I32, - IntegerKind::I64 => &I64, - IntegerKind::I128 => &I128, - IntegerKind::Isize => &ISIZE, - IntegerKind::U8 => &U8, - IntegerKind::U16 => &U16, - IntegerKind::U32 => &U32, - IntegerKind::U64 => &U64, - IntegerKind::U128 => &U128, - IntegerKind::Usize => &USIZE, + IntegerKind::Untyped => &UntypedIntegerTypeData, + IntegerKind::I8 => &I8TypeData, + IntegerKind::I16 => &I16TypeData, + IntegerKind::I32 => &I32TypeData, + IntegerKind::I64 => &I64TypeData, + IntegerKind::I128 => &I128TypeData, + IntegerKind::Isize => &IsizeTypeData, + IntegerKind::U8 => &U8TypeData, + IntegerKind::U16 => &U16TypeData, + IntegerKind::U32 => &U32TypeData, + IntegerKind::U64 => &U64TypeData, + IntegerKind::U128 => &U128TypeData, + IntegerKind::Usize => &UsizeTypeData, } } } @@ -286,15 +293,7 @@ pub(crate) enum FloatKind { F64, } -impl IsSpecificValueKind for FloatKind { - fn display_name(&self) -> &'static str { - match self { - FloatKind::Untyped => "untyped float", - FloatKind::F32 => "f32", - FloatKind::F64 => "f64", - } - } - +impl IsSpecificLeafKind for FloatKind { fn articled_display_name(&self) -> &'static str { match self { FloatKind::Untyped => "an untyped float", @@ -310,106 +309,119 @@ impl From for ValueKind { } } -static FLOAT: FloatTypeData = FloatTypeData; -static UNTYPED_FLOAT: UntypedFloatTypeData = UntypedFloatTypeData; -static F32: F32TypeData = F32TypeData; -static F64: F64TypeData = F64TypeData; - impl FloatKind { pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { match self { - FloatKind::Untyped => &UNTYPED_FLOAT, - FloatKind::F32 => &F32, - FloatKind::F64 => &F64, + FloatKind::Untyped => &UntypedFloatTypeData, + FloatKind::F32 => &F32TypeData, + FloatKind::F64 => &F64TypeData, } } } -pub(crate) struct Type { - span: Span, - pub(crate) kind: TypeKind, -} - // A ValueKind represents a kind of leaf value. // But a TypeKind represents a type in the hierarchy, which points at a type data. pub(crate) enum TypeKind { + Leaf(ValueKind), + Parent(ParentTypeKind), + Dyn(DynTypeKind), +} + +impl TypeKind { + pub(crate) fn from_source_name(name: &str) -> Option { + if let Some(kind) = ValueKind::from_source_name(name) { + return Some(TypeKind::Leaf(kind)); + } + if let Some(kind) = ParentTypeKind::from_source_name(name) { + return Some(TypeKind::Parent(kind)); + } + if let Some(kind) = DynTypeKind::from_source_name(name) { + return Some(TypeKind::Dyn(kind)); + } + None + } + + pub(crate) fn source_name(&self) -> &'static str { + match self { + TypeKind::Leaf(leaf_kind) => leaf_kind.source_name(), + TypeKind::Parent(parent_kind) => parent_kind.source_name(), + TypeKind::Dyn(dyn_kind) => dyn_kind.source_name(), + } + } +} + +pub(crate) enum ParentTypeKind { + Value, Integer, - SpecificInteger(IntegerKind), Float, - SpecificFloat(FloatKind), - Boolean, - String, - Char, - Array, - Object, - Stream, - Range, - Iterable, - Iterator, - Parser, } -impl Type { - pub(crate) fn from_source_name(name: &str) -> Option { +impl ParentTypeKind { + pub(crate) fn from_source_name(name: &str) -> Option { Some(match name { - "int" => TypeKind::Integer, - "i8" => TypeKind::SpecificInteger(IntegerKind::I8), - "i16" => TypeKind::SpecificInteger(IntegerKind::I16), - "i32" => TypeKind::SpecificInteger(IntegerKind::I32), - "i64" => TypeKind::SpecificInteger(IntegerKind::I64), - "i128" => TypeKind::SpecificInteger(IntegerKind::I128), - "isize" => TypeKind::SpecificInteger(IntegerKind::Isize), - "u8" => TypeKind::SpecificInteger(IntegerKind::U8), - "u16" => TypeKind::SpecificInteger(IntegerKind::U16), - "u32" => TypeKind::SpecificInteger(IntegerKind::U32), - "u64" => TypeKind::SpecificInteger(IntegerKind::U64), - "u128" => TypeKind::SpecificInteger(IntegerKind::U128), - "usize" => TypeKind::SpecificInteger(IntegerKind::Usize), - "float" => TypeKind::Float, - "f32" => TypeKind::SpecificFloat(FloatKind::F32), - "f64" => TypeKind::SpecificFloat(FloatKind::F64), - "bool" => TypeKind::Boolean, - "string" => TypeKind::String, - "char" => TypeKind::Char, - "array" => TypeKind::Array, - "object" => TypeKind::Object, - "stream" => TypeKind::Stream, - "range" => TypeKind::Range, - "iterable" => TypeKind::Iterable, - "iterator" => TypeKind::Iterator, - "parser" => TypeKind::Parser, + "value" => ParentTypeKind::Value, + "int" => ParentTypeKind::Integer, + "float" => ParentTypeKind::Float, _ => return None, }) } pub(crate) fn source_name(&self) -> &'static str { - // This should be inverse of parse below - match &self.kind { - TypeKind::Integer => "int", - TypeKind::SpecificInteger(kind) => kind.display_name(), - TypeKind::Float => "float", - TypeKind::SpecificFloat(kind) => kind.display_name(), - TypeKind::Boolean => "bool", - TypeKind::String => "string", - TypeKind::Char => "char", - TypeKind::Array => "array", - TypeKind::Object => "object", - TypeKind::Stream => "stream", - TypeKind::Range => "range", - TypeKind::Iterable => "iterable", - TypeKind::Iterator => "iterator", - TypeKind::Parser => "parser", + match self { + ParentTypeKind::Value => "value", + ParentTypeKind::Integer => "int", + ParentTypeKind::Float => "float", + } + } + + pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { + match self { + ParentTypeKind::Value => &ValueTypeData, + ParentTypeKind::Integer => &IntegerTypeData, + ParentTypeKind::Float => &FloatTypeData, } } +} + +pub(crate) enum DynTypeKind { + Iterable, +} + +impl DynTypeKind { + pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { + match self { + DynTypeKind::Iterable => &IterableTypeData, + } + } + + pub(crate) fn from_source_name(name: &str) -> Option { + match name { + "iterable" => Some(DynTypeKind::Iterable), + _ => None, + } + } + + pub(crate) fn source_name(&self) -> &'static str { + match self { + DynTypeKind::Iterable => "iterable", + } + } +} + +pub(crate) struct TypeIdent { + span: Span, + pub(crate) kind: TypeKind, +} +impl TypeIdent { pub(crate) fn from_ident(ident: &Ident) -> ParseResult { let span = ident.span(); let name = ident.to_string(); - let kind = match Self::from_source_name(name.as_str()) { + let kind = match TypeKind::from_source_name(name.as_str()) { Some(kind) => kind, None => { let lower_case_name = name.to_lowercase(); - let error_message = match Self::from_source_name(&lower_case_name) { + let error_message = match TypeKind::from_source_name(&lower_case_name) { Some(_) => format!("Expected '{}'", lower_case_name), None => match lower_case_name.as_str() { "integer" => "Expected 'int'".to_string(), @@ -427,7 +439,7 @@ impl Type { } } -impl ParseSource for Type { +impl ParseSource for TypeIdent { fn parse(input: SourceParser) -> ParseResult { Self::from_ident(&input.parse()?) } @@ -440,26 +452,15 @@ impl ParseSource for Type { impl TypeKind { pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { match self { - TypeKind::Integer => &INTEGER, - TypeKind::SpecificInteger(integer_kind) => integer_kind.method_resolver(), - TypeKind::Float => &FLOAT, - TypeKind::SpecificFloat(float_kind) => float_kind.method_resolver(), - TypeKind::Boolean => &BOOLEAN, - TypeKind::String => &STRING, - TypeKind::Char => &CHAR, - TypeKind::Array => &ARRAY, - TypeKind::Object => &OBJECT, - TypeKind::Stream => &STREAM, - TypeKind::Range => &RANGE, - TypeKind::Iterable => &ITERABLE, - TypeKind::Iterator => &ITERATOR, - TypeKind::Parser => &PARSER, + TypeKind::Leaf(leaf_kind) => leaf_kind.method_resolver(), + TypeKind::Parent(parent_kind) => parent_kind.method_resolver(), + TypeKind::Dyn(dyn_kind) => dyn_kind.method_resolver(), } } } pub(crate) struct TypeProperty { - pub(crate) source_type: Type, + pub(crate) source_type: TypeIdent, _colons: Unused, pub(crate) property: Ident, } @@ -493,7 +494,7 @@ impl TypeProperty { )), None => self.type_err(format!( "Type '{}' has no property named '{}'", - self.source_type.source_name(), + self.source_type.kind.source_name(), self.property, )), } diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index 2dcc5370..f7f151b2 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -120,39 +120,28 @@ impl Spanned<&RangeValue> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum RangeKind { /// `start .. end` - Range, + FromTo, /// `start ..` - RangeFrom, + From, /// `.. end` - RangeTo, + To, /// `..` - RangeFull, + Full, /// `start ..= end` - RangeInclusive, + FromToInclusive, /// `..= end` - RangeToInclusive, + ToInclusive, } -impl IsSpecificValueKind for RangeKind { - fn display_name(&self) -> &'static str { - match self { - RangeKind::Range => "range start..end", - RangeKind::RangeFrom => "range start..", - RangeKind::RangeTo => "range ..end", - RangeKind::RangeFull => "range ..", - RangeKind::RangeInclusive => "range start..=end", - RangeKind::RangeToInclusive => "range ..=end", - } - } - +impl IsSpecificLeafKind for RangeKind { fn articled_display_name(&self) -> &'static str { match self { - RangeKind::Range => "a range start..end", - RangeKind::RangeFrom => "a range start..", - RangeKind::RangeTo => "a range ..end", - RangeKind::RangeFull => "a range ..", - RangeKind::RangeInclusive => "a range start..=end", - RangeKind::RangeToInclusive => "a range ..=end", + RangeKind::FromTo => "a range start..end", + RangeKind::From => "a range start..", + RangeKind::To => "a range ..end", + RangeKind::Full => "a range ..", + RangeKind::FromToInclusive => "a range start..=end", + RangeKind::ToInclusive => "a range ..=end", } } } @@ -288,12 +277,12 @@ pub(crate) enum RangeValueInner { impl RangeValueInner { fn kind(&self) -> RangeKind { match self { - Self::Range { .. } => RangeKind::Range, - Self::RangeFrom { .. } => RangeKind::RangeFrom, - Self::RangeTo { .. } => RangeKind::RangeTo, - Self::RangeFull { .. } => RangeKind::RangeFull, - Self::RangeInclusive { .. } => RangeKind::RangeInclusive, - Self::RangeToInclusive { .. } => RangeKind::RangeToInclusive, + Self::Range { .. } => RangeKind::FromTo, + Self::RangeFrom { .. } => RangeKind::From, + Self::RangeTo { .. } => RangeKind::To, + Self::RangeFull { .. } => RangeKind::Full, + Self::RangeInclusive { .. } => RangeKind::FromToInclusive, + Self::RangeToInclusive { .. } => RangeKind::ToInclusive, } } diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index c80a99e3..8f464fb1 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -187,9 +187,9 @@ impl EqualityContext for TypedEquality { Err(self.error_span.type_error(format!( "lhs{} is {}, but rhs{} is {}", path_str, - lhs.articled_value_type(), + lhs.articled_kind(), path_str, - rhs.articled_value_type() + rhs.articled_kind() ))) } @@ -756,7 +756,7 @@ impl Value { if !self.value_kind().supports_transparent_cloning() { return error_span_range.ownership_err(format!( "An owned value is required, but a reference was received, and {} does not support transparent cloning. You may wish to use .clone() explicitly.", - self.articled_value_type() + self.articled_kind() )); } Ok(self.clone()) @@ -816,7 +816,7 @@ impl Value { match self { Value::Array(array) => array.into_indexed(index), Value::Object(object) => object.into_indexed(index), - other => access.type_err(format!("Cannot index into a {}", other.value_type())), + other => access.type_err(format!("Cannot index into {}", other.articled_kind())), } } @@ -829,7 +829,7 @@ impl Value { match self { Value::Array(array) => array.index_mut(index), Value::Object(object) => object.index_mut(index, auto_create), - other => access.type_err(format!("Cannot index into a {}", other.value_type())), + other => access.type_err(format!("Cannot index into {}", other.articled_kind())), } } @@ -841,7 +841,7 @@ impl Value { match self { Value::Array(array) => array.index_ref(index), Value::Object(object) => object.index_ref(index), - other => access.type_err(format!("Cannot index into a {}", other.value_type())), + other => access.type_err(format!("Cannot index into {}", other.articled_kind())), } } @@ -849,8 +849,8 @@ impl Value { match self { Value::Object(object) => object.into_property(access), other => access.type_err(format!( - "Cannot access properties on a {}", - other.value_type() + "Cannot access properties on {}", + other.articled_kind() )), } } @@ -863,8 +863,8 @@ impl Value { match self { Value::Object(object) => object.property_mut(access, auto_create), other => access.type_err(format!( - "Cannot access properties on a {}", - other.value_type() + "Cannot access properties on {}", + other.articled_kind() )), } } @@ -873,8 +873,8 @@ impl Value { match self { Value::Object(object) => object.property_ref(access), other => access.type_err(format!( - "Cannot access properties on a {}", - other.value_type() + "Cannot access properties on {}", + other.articled_kind() )), } } From 0c1e9a021012d2be709df500252dddb8160dddf0 Mon Sep 17 00:00:00 2001 From: David Edey Date: Fri, 2 Jan 2026 19:57:17 +0100 Subject: [PATCH 06/58] refactor: Remove RangeKind, replace with RangeStructure --- .../type_resolution/value_kinds.rs | 22 ++----- src/expressions/values/iterable.rs | 2 +- src/expressions/values/range.rs | 49 +++++++-------- src/expressions/values/value.rs | 62 ++++++++++++++++++- .../expressions/cast_int_to_bool.stderr | 2 +- .../fix_me_negative_max_int_fails.stderr | 2 +- .../operations/logical_not_on_int.stderr | 2 +- .../operations/negate_unsigned.stderr | 2 +- 8 files changed, 94 insertions(+), 49 deletions(-) diff --git a/src/expressions/type_resolution/value_kinds.rs b/src/expressions/type_resolution/value_kinds.rs index c59dbe56..cc284ab8 100644 --- a/src/expressions/type_resolution/value_kinds.rs +++ b/src/expressions/type_resolution/value_kinds.rs @@ -49,7 +49,7 @@ pub(crate) enum ValueKind { Array, Object, Stream, - Range(RangeKind), + Range, Iterator, Parser, } @@ -81,12 +81,7 @@ impl ValueKind { "array" => ValueKind::Array, "object" => ValueKind::Object, "stream" => ValueKind::Stream, - "range_from_to" => ValueKind::Range(RangeKind::FromTo), - "range_from" => ValueKind::Range(RangeKind::From), - "range_to" => ValueKind::Range(RangeKind::To), - "range_full" => ValueKind::Range(RangeKind::Full), - "range_from_to_inclusive" => ValueKind::Range(RangeKind::FromToInclusive), - "range_to_inclusive" => ValueKind::Range(RangeKind::ToInclusive), + "range" => ValueKind::Range, "iterator" => ValueKind::Iterator, "parser" => ValueKind::Parser, _ => return None, @@ -119,12 +114,7 @@ impl ValueKind { ValueKind::Array => "array", ValueKind::Object => "object", ValueKind::Stream => "stream", - ValueKind::Range(RangeKind::FromTo) => "range_from_to", - ValueKind::Range(RangeKind::From) => "range_from", - ValueKind::Range(RangeKind::To) => "range_to", - ValueKind::Range(RangeKind::Full) => "range_full", - ValueKind::Range(RangeKind::FromToInclusive) => "range_from_to_inclusive", - ValueKind::Range(RangeKind::ToInclusive) => "range_to_inclusive", + ValueKind::Range => "range", ValueKind::Iterator => "iterator", ValueKind::Parser => "parser", } @@ -145,7 +135,7 @@ impl IsSpecificLeafKind for ValueKind { ValueKind::Array => "an array", ValueKind::Object => "an object", ValueKind::Stream => "a stream", - ValueKind::Range(kind) => kind.articled_display_name(), + ValueKind::Range => "a range", ValueKind::Iterator => "an iterator", ValueKind::Parser => "a parser", } @@ -165,7 +155,7 @@ impl ValueKind { ValueKind::Array => &ArrayTypeData, ValueKind::Object => &ObjectTypeData, ValueKind::Stream => &StreamTypeData, - ValueKind::Range(_) => &RangeTypeData, + ValueKind::Range => &RangeTypeData, ValueKind::Iterator => &IteratorTypeData, ValueKind::Parser => &ParserTypeData, } @@ -190,7 +180,7 @@ impl ValueKind { ValueKind::Array => false, ValueKind::Object => false, ValueKind::Stream => false, - ValueKind::Range(_) => true, + ValueKind::Range => true, ValueKind::Iterator => false, // A parser is a handle, so can be cloned transparently. // It may fail to be able to be used to parse if the underlying stream is out of scope of course. diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index b50f8ff7..b6641382 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -123,7 +123,7 @@ impl IsArgument for IterableRef<'static> { ValueKind::Iterator => IterableRef::Iterator(IsArgument::from_argument(argument)?), ValueKind::Array => IterableRef::Array(IsArgument::from_argument(argument)?), ValueKind::Stream => IterableRef::Stream(IsArgument::from_argument(argument)?), - ValueKind::Range(_) => IterableRef::Range(IsArgument::from_argument(argument)?), + ValueKind::Range => IterableRef::Range(IsArgument::from_argument(argument)?), ValueKind::Object => IterableRef::Object(IsArgument::from_argument(argument)?), ValueKind::String => IterableRef::String(IsArgument::from_argument(argument)?), _ => { diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index f7f151b2..7681e2a3 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -118,7 +118,7 @@ impl Spanned<&RangeValue> { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum RangeKind { +pub(crate) enum RangeStructure { /// `start .. end` FromTo, /// `start ..` @@ -133,30 +133,24 @@ pub(crate) enum RangeKind { ToInclusive, } -impl IsSpecificLeafKind for RangeKind { - fn articled_display_name(&self) -> &'static str { +impl RangeStructure { + pub(crate) fn articled_display_name(&self) -> &'static str { match self { - RangeKind::FromTo => "a range start..end", - RangeKind::From => "a range start..", - RangeKind::To => "a range ..end", - RangeKind::Full => "a range ..", - RangeKind::FromToInclusive => "a range start..=end", - RangeKind::ToInclusive => "a range ..=end", + RangeStructure::FromTo => "a range start..end", + RangeStructure::From => "a range start..", + RangeStructure::To => "a range ..end", + RangeStructure::Full => "a range ..", + RangeStructure::FromToInclusive => "a range start..=end", + RangeStructure::ToInclusive => "a range ..=end", } } } -impl From for ValueKind { - fn from(kind: RangeKind) -> Self { - ValueKind::Range(kind) - } -} - impl HasValueKind for RangeValue { - type SpecificKind = RangeKind; + type SpecificKind = ValueKind; - fn kind(&self) -> RangeKind { - self.inner.kind() + fn kind(&self) -> ValueKind { + ValueKind::Range } } @@ -233,7 +227,10 @@ impl ValuesEqual for RangeValue { .. }, ) => ctx.with_range_end(|ctx| l_end.test_equality(r_end, ctx)), - _ => ctx.kind_mismatch(self, other), + _ => ctx.range_structure_mismatch( + self.inner.structure_kind(), + other.inner.structure_kind(), + ), } } } @@ -275,14 +272,14 @@ pub(crate) enum RangeValueInner { } impl RangeValueInner { - fn kind(&self) -> RangeKind { + fn structure_kind(&self) -> RangeStructure { match self { - Self::Range { .. } => RangeKind::FromTo, - Self::RangeFrom { .. } => RangeKind::From, - Self::RangeTo { .. } => RangeKind::To, - Self::RangeFull { .. } => RangeKind::Full, - Self::RangeInclusive { .. } => RangeKind::FromToInclusive, - Self::RangeToInclusive { .. } => RangeKind::ToInclusive, + Self::Range { .. } => RangeStructure::FromTo, + Self::RangeFrom { .. } => RangeStructure::From, + Self::RangeTo { .. } => RangeStructure::To, + Self::RangeFull { .. } => RangeStructure::Full, + Self::RangeInclusive { .. } => RangeStructure::FromToInclusive, + Self::RangeToInclusive { .. } => RangeStructure::ToInclusive, } } diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index 8f464fb1..ed9838b9 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -52,10 +52,17 @@ pub(crate) trait EqualityContext { /// Values of the same type are not equal. fn leaf_values_not_equal(&mut self, lhs: &T, rhs: &T) -> Self::Result; - /// Values have different types. + /// Values have different kinds. fn kind_mismatch(&mut self, lhs: &L, rhs: &R) -> Self::Result; + /// Range values have different structures. + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Self::Result; + /// Arrays or iterators have different lengths. fn lengths_unequal(&mut self, lhs_len: Option, rhs_len: Option) -> Self::Result; @@ -109,6 +116,11 @@ impl EqualityContext for SimpleEquality { false } + #[inline] + fn range_structure_mismatch(&mut self, _lhs: RangeStructure, _rhs: RangeStructure) -> bool { + false + } + #[inline] fn lengths_unequal(&mut self, _lhs_len: Option, _rhs_len: Option) -> bool { false @@ -193,6 +205,21 @@ impl EqualityContext for TypedEquality { ))) } + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Self::Result { + let path_str = PathSegment::fmt_path(&self.path); + Err(self.error_span.type_error(format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs.articled_display_name(), + path_str, + rhs.articled_display_name() + ))) + } + #[inline] fn lengths_unequal( &mut self, @@ -279,6 +306,11 @@ pub(crate) enum DebugInequalityReason { lhs_kind: ValueKind, rhs_kind: ValueKind, }, + /// Ranges have incompatible structures. + RangeStructureMismatch { + lhs_structure: RangeStructure, + rhs_structure: RangeStructure, + }, /// Collections have different lengths. LengthMismatch { lhs_len: Option, @@ -324,6 +356,18 @@ impl DebugEqualityError { rhs_kind.articled_display_name() ) } + DebugInequalityReason::RangeStructureMismatch { + lhs_structure: lhs_kind, + rhs_structure: rhs_kind, + } => { + format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs_kind.articled_display_name(), + path_str, + rhs_kind.articled_display_name() + ) + } DebugInequalityReason::LengthMismatch { lhs_len, rhs_len } => { format!( "lhs{} has length {}, but rhs{} has length {}", @@ -409,6 +453,20 @@ impl EqualityContext for DebugEquality { })? } + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::RangeStructureMismatch { + lhs_structure: lhs, + rhs_structure: rhs, + }, + })? + } + #[inline] fn lengths_unequal( &mut self, @@ -1119,7 +1177,7 @@ impl HasValueKind for Value { Value::Array(_) => ValueKind::Array, Value::Object(_) => ValueKind::Object, Value::Stream(_) => ValueKind::Stream, - Value::Range(range) => ValueKind::Range(range.kind()), + Value::Range(_) => ValueKind::Range, Value::Iterator(_) => ValueKind::Iterator, Value::Parser(_) => ValueKind::Parser, Value::UnsupportedLiteral(_) => ValueKind::UnsupportedLiteral, diff --git a/tests/compilation_failures/expressions/cast_int_to_bool.stderr b/tests/compilation_failures/expressions/cast_int_to_bool.stderr index 52aaf6bd..b8cfc15b 100644 --- a/tests/compilation_failures/expressions/cast_int_to_bool.stderr +++ b/tests/compilation_failures/expressions/cast_int_to_bool.stderr @@ -1,4 +1,4 @@ -error: The as bool operator is not supported for untyped integer values +error: The as bool operator is not supported for an untyped integer --> tests/compilation_failures/expressions/cast_int_to_bool.rs:5:13 | 5 | #(1 as bool) diff --git a/tests/compilation_failures/expressions/fix_me_negative_max_int_fails.stderr b/tests/compilation_failures/expressions/fix_me_negative_max_int_fails.stderr index 20765e62..71c954bf 100644 --- a/tests/compilation_failures/expressions/fix_me_negative_max_int_fails.stderr +++ b/tests/compilation_failures/expressions/fix_me_negative_max_int_fails.stderr @@ -1,4 +1,4 @@ -error: The - operator is not supported for unsupported literal values +error: The - operator is not supported for an unsupported literal --> tests/compilation_failures/expressions/fix_me_negative_max_int_fails.rs:8:11 | 8 | #(-128i8) diff --git a/tests/compilation_failures/operations/logical_not_on_int.stderr b/tests/compilation_failures/operations/logical_not_on_int.stderr index 49d0d36b..f9bf8831 100644 --- a/tests/compilation_failures/operations/logical_not_on_int.stderr +++ b/tests/compilation_failures/operations/logical_not_on_int.stderr @@ -1,4 +1,4 @@ -error: The ! operator is not supported for untyped integer values +error: The ! operator is not supported for an untyped integer --> tests/compilation_failures/operations/logical_not_on_int.rs:4:18 | 4 | let _ = run!(!5); diff --git a/tests/compilation_failures/operations/negate_unsigned.stderr b/tests/compilation_failures/operations/negate_unsigned.stderr index d257b1b1..5316148d 100644 --- a/tests/compilation_failures/operations/negate_unsigned.stderr +++ b/tests/compilation_failures/operations/negate_unsigned.stderr @@ -1,4 +1,4 @@ -error: The - operator is not supported for u32 values +error: The - operator is not supported for a u32 --> tests/compilation_failures/operations/negate_unsigned.rs:4:18 | 4 | let _ = run!(-5u32); From e77e355f1f6eb804aeba9a9030e4e44358bd1e8e Mon Sep 17 00:00:00 2001 From: David Edey Date: Fri, 2 Jan 2026 23:16:27 +0100 Subject: [PATCH 07/58] feat: Further type macro additions --- plans/TODO.md | 5 +- src/expressions/concepts/dyn_impls.rs | 7 +- src/expressions/concepts/type_impls.rs | 46 ++++-- src/expressions/concepts/type_traits.rs | 156 +++++++++++++++--- src/expressions/type_resolution/type_data.rs | 2 +- .../type_resolution/value_kinds.rs | 37 ++--- 6 files changed, 194 insertions(+), 59 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index d0dcc2c9..ce4753bf 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -206,7 +206,10 @@ First, read the @./2025-11-vision.md - [x] Improved error handling in the macro (e.g. required arg after optional; no matching strongly-typed signature) - [x] Separate Hierarchical and DynCompatible Forms - [x] Improved macro support - - [ ] Add source type name to type macro/s + - [x] Add source type name to type macro/s + - [ ] Add (temporary) ability to link to TypeData and resolve methods from there + - [ ] Then implement all the macros + - [ ] And use that to generate ValueKind from the new macros - [ ] Add ability to implement IsIterable - [ ] `CastTarget` simply wraps `TypeKind` - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): diff --git a/src/expressions/concepts/dyn_impls.rs b/src/expressions/concepts/dyn_impls.rs index 03084029..986dbc3e 100644 --- a/src/expressions/concepts/dyn_impls.rs +++ b/src/expressions/concepts/dyn_impls.rs @@ -6,6 +6,9 @@ pub(crate) trait IsIterable: 'static { } define_dyn_type!( - dyn IsIterable => "an iterable (e.g. array, list, etc.)", - pub(crate) IterableType + pub(crate) IterableType, + content: dyn IsIterable, + dyn_kind: DynTypeKind::Iterable, + type_name: "iterable", + articled_display_name: "an iterable (e.g. array, list, etc.)", ); diff --git a/src/expressions/concepts/type_impls.rs b/src/expressions/concepts/type_impls.rs index fa82d2fc..0b366f2c 100644 --- a/src/expressions/concepts/type_impls.rs +++ b/src/expressions/concepts/type_impls.rs @@ -7,38 +7,58 @@ pub(crate) type QqqValueReferencable = Actual<'static, ValueType, BeReferenceabl pub(crate) type QqqValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; pub(crate) type QqqValueMut<'a> = Actual<'a, ValueType, BeAnyMut>; +impl From for ValueKind { + fn from(_kind: XxxValueKind) -> Self { + unimplemented!() + } +} + define_parent_type! { pub(crate) ValueType, - pub(crate) enum ValueContent { + content: pub(crate) ValueContent, + kind: pub(crate) XxxValueKind, + parent_kind: ParentTypeKind::Value, + variants: { Integer => IntegerType, Object => ObjectType, }, - "any value", + type_name: "value", + articled_display_name: "any value", } define_parent_type! { pub(crate) IntegerType => ValueType(ValueContent::Integer), - pub(crate) enum IntegerContent { + content: pub(crate) IntegerContent, + kind: pub(crate) XxxIntegerKind, + parent_kind: ParentTypeKind::Integer, + variants: { U32 => U32Type, U64 => U64Type, }, - "an integer", + type_name: "integer", + articled_display_name: "an integer", } define_leaf_type! { - pub(crate) U32Type => IntegerType(IntegerContent::U32, IntegerKind::U32) => ValueType, - u32, - "a u32", + pub(crate) U32Type => IntegerType(IntegerContent::U32) => ValueType, + content: u32, + kind: pub(crate) U32Kind, + type_name: "u32", + articled_display_name: "a u32", } define_leaf_type! { - pub(crate) U64Type => IntegerType(IntegerContent::U64, IntegerKind::U64) => ValueType, - u64, - "a u64", + pub(crate) U64Type => IntegerType(IntegerContent::U64) => ValueType, + content: u64, + kind: pub(crate) U64Kind, + type_name: "u64", + articled_display_name: "a u64", } define_leaf_type! { - pub(crate) ObjectType => ValueType(ValueContent::Object, ValueKind::Object), - ObjectValue, - "an object", + pub(crate) ObjectType => ValueType(ValueContent::Object), + content: ObjectValue, + kind: pub(crate) ObjectKind, + type_name: "object", + articled_display_name: "an object", } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index ceca846e..e65ad6fd 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -11,12 +11,16 @@ impl TypeVariant for DynTypeVariant {} pub(crate) trait IsType: Sized { type Variant: TypeVariant; - fn articled_type_name() -> &'static str; + const SOURCE_TYPE_NAME: &'static str; + const ARTICLED_DISPLAY_NAME: &'static str; + + fn type_kind() -> TypeKind; } pub(crate) trait IsHierarchicalType: IsType { // >::Content<'a>> := Self::Content<'a, F> type Content<'a, F: IsHierarchicalForm>; + type LeafKind: IsSpecificLeafKind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( structure: Self::Content<'a, F>, @@ -58,8 +62,8 @@ pub(crate) trait DowncastFrom + IsFormOf>: IsTyp return span_range.value_err(format!( "{} is expected to be {}, but it is {}", resolution_target, - Self::articled_type_name(), - T::articled_type_name(), + Self::ARTICLED_DISPLAY_NAME, + T::ARTICLED_DISPLAY_NAME, )) } }; @@ -214,23 +218,55 @@ where macro_rules! define_parent_type { ( $type_def_vis:vis $type_def:ident $(=> $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?, - $content_vis:vis enum $content:ident { + content: $content_vis:vis $content:ident, + kind: $kind_vis:vis $kind:ident, + parent_kind: ParentTypeKind::$parent_kind:ident, + variants: { $($variant:ident => $variant_type:ty,)* }, - $articled_type_name:literal, + type_name: $source_type_name:literal, + articled_display_name: $articled_display_name:literal, ) => { $type_def_vis struct $type_def; impl IsType for $type_def { type Variant = HierarchicalTypeVariant; - fn articled_type_name() -> &'static str { - $articled_type_name + const SOURCE_TYPE_NAME: &'static str = $source_type_name; + const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; + + fn type_kind() -> TypeKind { + TypeKind::Parent(ParentTypeKind::$parent_kind) + } + } + + impl MethodResolver for $type_def { + fn resolve_method(&self, _method_name: &str) -> Option { + unimplemented!() + } + + fn resolve_unary_operation( + &self, + _operation: &UnaryOperation, + ) -> Option { + unimplemented!() + } + + fn resolve_binary_operation( + &self, + _operation: &BinaryOperation, + ) -> Option { + unimplemented!() + } + + fn resolve_type_property(&self, _property_name: &str) -> Option { + unimplemented!() } } impl IsHierarchicalType for $type_def { type Content<'a, F: IsHierarchicalForm> = $content<'a, F>; + type LeafKind = $kind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( content: Self::Content<'a, F>, @@ -245,6 +281,32 @@ macro_rules! define_parent_type { $( $variant(Actual<'a, $variant_type, F>), )* } + #[derive(Clone, Copy, PartialEq, Eq)] + $kind_vis enum $kind { + $( $variant(<$variant_type as IsHierarchicalType>::LeafKind), )* + } + + $( + impl From<$kind> for ValueKind { + fn from(kind: $kind) -> Self { + let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); + ValueKind::from(as_parent_kind) + } + } + )? + + impl IsSpecificLeafKind for $kind { + fn articled_display_name(&self) -> &'static str { + match self { + $( Self::$variant(x) => x.articled_display_name(), )* + } + } + + fn method_resolver(&self) -> &'static dyn MethodResolver { + &$type_def + } + } + impl_ancestor_chain_conversions!( $type_def $(=> $parent($parent_content :: $parent_variant) $(=> $ancestor)*)? ); @@ -255,28 +317,28 @@ pub(crate) use define_parent_type; macro_rules! define_leaf_type { ( - $type_def_vis:vis $type_def:ident => $parent:ident($parent_content:ident :: $parent_variant:ident, $leaf_kind_type:ident :: $leaf_kind_variant:ident) $(=> $ancestor:ty)*, - $leaf_type:ty, - $articled_type_name:literal, + $type_def_vis:vis $type_def:ident => $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*, + content: $leaf_type:ty, + kind: $kind_vis:vis $kind:ident, + type_name: $source_type_name:literal, + articled_display_name: $articled_display_name:literal, ) => { $type_def_vis struct $type_def; impl IsType for $type_def { type Variant = HierarchicalTypeVariant; - fn articled_type_name() -> &'static str { - $articled_type_name - } - } - - impl HasLeafKind for $type_def { - type LeafKindType = $leaf_kind_type; + const SOURCE_TYPE_NAME: &'static str = $source_type_name; + const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; - const KIND: Self::LeafKindType = $leaf_kind_type::$leaf_kind_variant; + fn type_kind() -> TypeKind { + TypeKind::Leaf(ValueKind::from($kind)) + } } impl IsHierarchicalType for $type_def { type Content<'a, F: IsHierarchicalForm> = F::Leaf<'a, $leaf_type>; + type LeafKind = $kind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( content: Self::Content<'a, F>, @@ -285,6 +347,50 @@ macro_rules! define_leaf_type { } } + impl MethodResolver for $type_def { + fn resolve_method(&self, _method_name: &str) -> Option { + unimplemented!() + } + + fn resolve_unary_operation( + &self, + _operation: &UnaryOperation, + ) -> Option { + unimplemented!() + } + + fn resolve_binary_operation( + &self, + _operation: &BinaryOperation, + ) -> Option { + unimplemented!() + } + + fn resolve_type_property(&self, _property_name: &str) -> Option { + unimplemented!() + } + } + + #[derive(Clone, Copy, PartialEq, Eq)] + $kind_vis struct $kind; + + impl From<$kind> for ValueKind { + fn from(kind: $kind) -> Self { + let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); + ValueKind::from(as_parent_kind) + } + } + + impl IsSpecificLeafKind for $kind { + fn articled_display_name(&self) -> &'static str { + $articled_display_name + } + + fn method_resolver(&self) -> &'static dyn MethodResolver { + &$type_def + } + } + impl<'a> IsValueContent<'a> for $leaf_type { type Type = $type_def; type Form = BeOwned; @@ -317,16 +423,22 @@ pub(crate) struct DynMapper(std::marker::PhantomData); macro_rules! define_dyn_type { ( - $dyn_type:ty => $articled_type_name:literal, - $type_def_vis:vis $type_def:ident + $type_def_vis:vis $type_def:ident, + content: $dyn_type:ty, + dyn_kind: DynTypeKind::$dyn_kind:ident, + type_name: $source_type_name:literal, + articled_display_name: $articled_display_name:literal, ) => { $type_def_vis struct $type_def; impl IsType for $type_def { type Variant = DynTypeVariant; - fn articled_type_name() -> &'static str { - $articled_type_name + const SOURCE_TYPE_NAME: &'static str = $source_type_name; + const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; + + fn type_kind() -> TypeKind { + TypeKind::Dyn(DynTypeKind::$dyn_kind) } } diff --git a/src/expressions/type_resolution/type_data.rs b/src/expressions/type_resolution/type_data.rs index 3cd8d070..d508d138 100644 --- a/src/expressions/type_resolution/type_data.rs +++ b/src/expressions/type_resolution/type_data.rs @@ -1,7 +1,7 @@ #![allow(clippy::type_complexity)] use super::*; -pub(in crate::expressions) trait MethodResolver { +pub(crate) trait MethodResolver { /// Resolves a unary operation as a method interface for this type. fn resolve_method(&self, method_name: &str) -> Option; diff --git a/src/expressions/type_resolution/value_kinds.rs b/src/expressions/type_resolution/value_kinds.rs index cc284ab8..7099881e 100644 --- a/src/expressions/type_resolution/value_kinds.rs +++ b/src/expressions/type_resolution/value_kinds.rs @@ -3,6 +3,7 @@ use super::*; /// A trait for specific value kinds that can provide a display name. /// This is implemented by `ValueKind`, `IntegerKind`, `FloatKind`, etc. pub(crate) trait IsSpecificLeafKind: Copy + Into { + fn method_resolver(&self) -> &'static dyn MethodResolver; fn articled_display_name(&self) -> &'static str; } @@ -140,9 +141,7 @@ impl IsSpecificLeafKind for ValueKind { ValueKind::Parser => "a parser", } } -} -impl ValueKind { fn method_resolver(&self) -> &'static dyn MethodResolver { match self { ValueKind::None => &NoneTypeData, @@ -160,7 +159,9 @@ impl ValueKind { ValueKind::Parser => &ParserTypeData, } } +} +impl ValueKind { /// This should be true for types which users expect to have value /// semantics, but false for types which are expensive to clone or /// are expected to have reference semantics. @@ -248,16 +249,8 @@ impl IsSpecificLeafKind for IntegerKind { IntegerKind::Usize => "a usize", } } -} -impl From for ValueKind { - fn from(kind: IntegerKind) -> Self { - ValueKind::Integer(kind) - } -} - -impl IntegerKind { - pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { + fn method_resolver(&self) -> &'static dyn MethodResolver { match self { IntegerKind::Untyped => &UntypedIntegerTypeData, IntegerKind::I8 => &I8TypeData, @@ -276,6 +269,12 @@ impl IntegerKind { } } +impl From for ValueKind { + fn from(kind: IntegerKind) -> Self { + ValueKind::Integer(kind) + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum FloatKind { Untyped, @@ -291,16 +290,8 @@ impl IsSpecificLeafKind for FloatKind { FloatKind::F64 => "an f64", } } -} - -impl From for ValueKind { - fn from(kind: FloatKind) -> Self { - ValueKind::Float(kind) - } -} -impl FloatKind { - pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { + fn method_resolver(&self) -> &'static dyn MethodResolver { match self { FloatKind::Untyped => &UntypedFloatTypeData, FloatKind::F32 => &F32TypeData, @@ -309,6 +300,12 @@ impl FloatKind { } } +impl From for ValueKind { + fn from(kind: FloatKind) -> Self { + ValueKind::Float(kind) + } +} + // A ValueKind represents a kind of leaf value. // But a TypeKind represents a type in the hierarchy, which points at a type data. pub(crate) enum TypeKind { From 0fd758a0766a1e860c099b782b2eeb1435c2bb90 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 3 Jan 2026 00:20:39 +0100 Subject: [PATCH 08/58] feat: Add type macro for each value --- plans/TODO.md | 6 +- src/expressions/concepts/mod.rs | 2 - src/expressions/concepts/type_impls.rs | 64 -- src/expressions/concepts/type_traits.rs | 65 +- src/expressions/equality.rs | 579 +++++++++++++++++ src/expressions/mod.rs | 2 + src/expressions/operations.rs | 6 +- .../type_resolution/value_kinds.rs | 26 +- src/expressions/values/array.rs | 9 + src/expressions/values/boolean.rs | 9 + src/expressions/values/character.rs | 9 + src/expressions/values/float.rs | 15 + src/expressions/values/float_subtypes.rs | 23 +- src/expressions/values/float_untyped.rs | 9 + src/expressions/values/integer.rs | 25 + src/expressions/values/integer_subtypes.rs | 75 ++- src/expressions/values/integer_untyped.rs | 9 + src/expressions/values/iterator.rs | 9 + src/expressions/values/none.rs | 10 + src/expressions/values/object.rs | 9 + src/expressions/values/parser.rs | 9 + src/expressions/values/range.rs | 9 + src/expressions/values/stream.rs | 9 + src/expressions/values/string.rs | 9 + src/expressions/values/unsupported_literal.rs | 9 + src/expressions/values/value.rs | 611 ++---------------- 26 files changed, 910 insertions(+), 707 deletions(-) delete mode 100644 src/expressions/concepts/type_impls.rs create mode 100644 src/expressions/equality.rs diff --git a/plans/TODO.md b/plans/TODO.md index ce4753bf..ab5d23fa 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -207,8 +207,10 @@ First, read the @./2025-11-vision.md - [x] Separate Hierarchical and DynCompatible Forms - [x] Improved macro support - [x] Add source type name to type macro/s - - [ ] Add (temporary) ability to link to TypeData and resolve methods from there - - [ ] Then implement all the macros + - [ ] Generate value kinds from the macros + - [x] Add (temporary) ability to link to TypeData and resolve methods from there + - [x] Then implement all the macros + - [ ] Add other methods to e.g. value kinds - i.e. Work out some way to _parse_ the value/type kinds - [ ] And use that to generate ValueKind from the new macros - [ ] Add ability to implement IsIterable - [ ] `CastTarget` simply wraps `TypeKind` diff --git a/src/expressions/concepts/mod.rs b/src/expressions/concepts/mod.rs index 17ddf9f8..26750a00 100644 --- a/src/expressions/concepts/mod.rs +++ b/src/expressions/concepts/mod.rs @@ -5,12 +5,10 @@ mod actual; mod dyn_impls; mod form; mod forms; -mod type_impls; mod type_traits; pub(crate) use actual::*; pub(crate) use dyn_impls::*; pub(crate) use form::*; pub(crate) use forms::*; -pub(crate) use type_impls::*; pub(crate) use type_traits::*; diff --git a/src/expressions/concepts/type_impls.rs b/src/expressions/concepts/type_impls.rs deleted file mode 100644 index 0b366f2c..00000000 --- a/src/expressions/concepts/type_impls.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::*; - -// NOTE: These should be moved out to the value definitions soon - -pub(crate) type QqqValue = Actual<'static, ValueType, BeOwned>; -pub(crate) type QqqValueReferencable = Actual<'static, ValueType, BeReferenceable>; -pub(crate) type QqqValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; -pub(crate) type QqqValueMut<'a> = Actual<'a, ValueType, BeAnyMut>; - -impl From for ValueKind { - fn from(_kind: XxxValueKind) -> Self { - unimplemented!() - } -} - -define_parent_type! { - pub(crate) ValueType, - content: pub(crate) ValueContent, - kind: pub(crate) XxxValueKind, - parent_kind: ParentTypeKind::Value, - variants: { - Integer => IntegerType, - Object => ObjectType, - }, - type_name: "value", - articled_display_name: "any value", -} - -define_parent_type! { - pub(crate) IntegerType => ValueType(ValueContent::Integer), - content: pub(crate) IntegerContent, - kind: pub(crate) XxxIntegerKind, - parent_kind: ParentTypeKind::Integer, - variants: { - U32 => U32Type, - U64 => U64Type, - }, - type_name: "integer", - articled_display_name: "an integer", -} - -define_leaf_type! { - pub(crate) U32Type => IntegerType(IntegerContent::U32) => ValueType, - content: u32, - kind: pub(crate) U32Kind, - type_name: "u32", - articled_display_name: "a u32", -} - -define_leaf_type! { - pub(crate) U64Type => IntegerType(IntegerContent::U64) => ValueType, - content: u64, - kind: pub(crate) U64Kind, - type_name: "u64", - articled_display_name: "a u64", -} - -define_leaf_type! { - pub(crate) ObjectType => ValueType(ValueContent::Object), - content: ObjectValue, - kind: pub(crate) ObjectKind, - type_name: "object", - articled_display_name: "an object", -} diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index e65ad6fd..06ee3657 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -219,13 +219,14 @@ macro_rules! define_parent_type { ( $type_def_vis:vis $type_def:ident $(=> $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?, content: $content_vis:vis $content:ident, - kind: $kind_vis:vis $kind:ident, - parent_kind: ParentTypeKind::$parent_kind:ident, + leaf_kind: $leaf_kind_vis:vis $leaf_kind:ident, + type_kind: ParentTypeKind::$parent_kind:ident($type_kind_vis:vis $type_kind:ident), variants: { $($variant:ident => $variant_type:ty,)* }, type_name: $source_type_name:literal, articled_display_name: $articled_display_name:literal, + temp_type_data: $type_data:ident, ) => { $type_def_vis struct $type_def; @@ -236,37 +237,37 @@ macro_rules! define_parent_type { const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; fn type_kind() -> TypeKind { - TypeKind::Parent(ParentTypeKind::$parent_kind) + TypeKind::Parent(ParentTypeKind::$parent_kind($type_kind)) } } impl MethodResolver for $type_def { - fn resolve_method(&self, _method_name: &str) -> Option { - unimplemented!() + fn resolve_method(&self, method_name: &str) -> Option { + $type_data.resolve_method(method_name) } fn resolve_unary_operation( &self, - _operation: &UnaryOperation, + operation: &UnaryOperation, ) -> Option { - unimplemented!() + $type_data.resolve_unary_operation(operation) } fn resolve_binary_operation( &self, - _operation: &BinaryOperation, + operation: &BinaryOperation, ) -> Option { - unimplemented!() + $type_data.resolve_binary_operation(operation) } - fn resolve_type_property(&self, _property_name: &str) -> Option { - unimplemented!() + fn resolve_type_property(&self, property_name: &str) -> Option { + $type_data.resolve_type_property(property_name) } } impl IsHierarchicalType for $type_def { type Content<'a, F: IsHierarchicalForm> = $content<'a, F>; - type LeafKind = $kind; + type LeafKind = $leaf_kind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( content: Self::Content<'a, F>, @@ -282,20 +283,31 @@ macro_rules! define_parent_type { } #[derive(Clone, Copy, PartialEq, Eq)] - $kind_vis enum $kind { + $type_kind_vis struct $type_kind; + + impl $type_kind { + pub(crate) const SOURCE_TYPE_NAME: &'static str = $source_type_name; + + pub(crate) fn method_resolver(&self) -> &'static dyn MethodResolver { + &$type_def + } + } + + #[derive(Clone, Copy, PartialEq, Eq)] + $leaf_kind_vis enum $leaf_kind { $( $variant(<$variant_type as IsHierarchicalType>::LeafKind), )* } $( - impl From<$kind> for ValueKind { - fn from(kind: $kind) -> Self { + impl From<$leaf_kind> for ValueKind { + fn from(kind: $leaf_kind) -> Self { let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); ValueKind::from(as_parent_kind) } } )? - impl IsSpecificLeafKind for $kind { + impl IsSpecificLeafKind for $leaf_kind { fn articled_display_name(&self) -> &'static str { match self { $( Self::$variant(x) => x.articled_display_name(), )* @@ -303,7 +315,9 @@ macro_rules! define_parent_type { } fn method_resolver(&self) -> &'static dyn MethodResolver { - &$type_def + match self { + $( Self::$variant(x) => x.method_resolver(), )* + } } } @@ -322,6 +336,7 @@ macro_rules! define_leaf_type { kind: $kind_vis:vis $kind:ident, type_name: $source_type_name:literal, articled_display_name: $articled_display_name:literal, + temp_type_data: $type_data:ident, ) => { $type_def_vis struct $type_def; @@ -348,26 +363,26 @@ macro_rules! define_leaf_type { } impl MethodResolver for $type_def { - fn resolve_method(&self, _method_name: &str) -> Option { - unimplemented!() + fn resolve_method(&self, method_name: &str) -> Option { + $type_data.resolve_method(method_name) } fn resolve_unary_operation( &self, - _operation: &UnaryOperation, + operation: &UnaryOperation, ) -> Option { - unimplemented!() + $type_data.resolve_unary_operation(operation) } fn resolve_binary_operation( &self, - _operation: &BinaryOperation, + operation: &BinaryOperation, ) -> Option { - unimplemented!() + $type_data.resolve_binary_operation(operation) } - fn resolve_type_property(&self, _property_name: &str) -> Option { - unimplemented!() + fn resolve_type_property(&self, property_name: &str) -> Option { + $type_data.resolve_type_property(property_name) } } diff --git a/src/expressions/equality.rs b/src/expressions/equality.rs new file mode 100644 index 00000000..708cefb8 --- /dev/null +++ b/src/expressions/equality.rs @@ -0,0 +1,579 @@ +use super::*; + +// ============================================================================ +// Equality Context - Controls behavior of value equality comparisons +// ============================================================================ + +/// A segment in the path to the current comparison location. +#[derive(Clone, Debug)] +pub(crate) enum PathSegment { + ArrayIndex(usize), + ObjectKey(String), + IteratorIndex(usize), + RangeStart, + RangeEnd, +} + +impl PathSegment { + fn fmt_path(path: &[PathSegment]) -> String { + let mut result = String::new(); + for segment in path { + match segment { + PathSegment::ArrayIndex(i) => result.push_str(&format!("[{}]", i)), + PathSegment::ObjectKey(k) => { + if syn::parse_str::(k).is_ok() { + result.push_str(&format!(".{}", k)) + } else { + result.push_str(&format!("[{:?}]", k)) + } + } + PathSegment::IteratorIndex(i) => result.push_str(&format!("[{}]", i)), + PathSegment::RangeStart => result.push_str(".start"), + PathSegment::RangeEnd => result.push_str(".end"), + } + } + result + } +} + +/// Context trait for controlling equality comparison behavior. +/// +/// Different implementations allow for: +/// - Simple equality: returns `false` on type mismatch (like JS `===`) +/// - Typed equality: errors on type mismatch, tracks path for error messages +/// - Assert equality: errors on any difference, useful for assert_eq +pub(crate) trait EqualityContext { + /// The result type returned by equality comparisons. + type Result; + + /// Values are equal. + fn values_equal(&mut self) -> Self::Result; + + /// Values of the same type are not equal. + fn leaf_values_not_equal(&mut self, lhs: &T, rhs: &T) -> Self::Result; + + /// Values have different kinds. + fn kind_mismatch(&mut self, lhs: &L, rhs: &R) + -> Self::Result; + + /// Range values have different structures. + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Self::Result; + + /// Arrays or iterators have different lengths. + fn lengths_unequal(&mut self, lhs_len: Option, rhs_len: Option) -> Self::Result; + + /// Object is missing a key that the other has. + fn missing_key(&mut self, key: &str, missing_on: MissingSide) -> Self::Result; + + fn iteration_limit_exceeded(&mut self, limit: usize) -> Self::Result { + let message = format!("iteration limit {} exceeded", limit); + self.leaf_values_not_equal(&message, &message) + } + + /// Wrap a comparison within an array index context. + fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R; + + /// Wrap a comparison within an object key context. + fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R; + + /// Wrap a comparison within an iterator index context. + fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R; + + /// Wrap a comparison within a range start context. + fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R; + + /// Wrap a comparison within a range end context. + fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R; + + /// Returns true if the result indicates we should stop comparing and return early. + /// This is true when the result indicates "not equal" or an error occurred. + fn should_short_circuit(&self, result: &Self::Result) -> bool; +} + +/// Simple equality context - returns `false` on type mismatch, no path tracking. +/// This is the most efficient option when you just need a bool result. +pub(crate) struct SimpleEquality; + +impl EqualityContext for SimpleEquality { + type Result = bool; + + #[inline] + fn values_equal(&mut self) -> bool { + true + } + + #[inline] + fn leaf_values_not_equal(&mut self, _lhs: &T, _rhs: &T) -> bool { + false + } + + #[inline] + fn kind_mismatch(&mut self, _lhs: &L, _rhs: &R) -> bool { + false + } + + #[inline] + fn range_structure_mismatch(&mut self, _lhs: RangeStructure, _rhs: RangeStructure) -> bool { + false + } + + #[inline] + fn lengths_unequal(&mut self, _lhs_len: Option, _rhs_len: Option) -> bool { + false + } + + #[inline] + fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> bool { + false + } + + #[inline] + fn with_array_index(&mut self, _index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + f(self) + } + + #[inline] + fn with_object_key(&mut self, _key: &str, f: impl FnOnce(&mut Self) -> R) -> R { + f(self) + } + + #[inline] + fn with_iterator_index(&mut self, _index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + f(self) + } + + #[inline] + fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + f(self) + } + + #[inline] + fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + f(self) + } + + #[inline] + fn should_short_circuit(&self, result: &bool) -> bool { + !*result + } +} + +/// Typed equality context - errors on type mismatch, tracks path for error messages. +pub(crate) struct TypedEquality { + path: Vec, + error_span: SpanRange, +} + +impl TypedEquality { + pub(crate) fn new(error_span: SpanRange) -> Self { + Self { + path: Vec::new(), + error_span, + } + } +} + +impl EqualityContext for TypedEquality { + type Result = ExecutionResult; + + #[inline] + fn values_equal(&mut self) -> ExecutionResult { + Ok(true) + } + + #[inline] + fn leaf_values_not_equal(&mut self, _lhs: &T, _rhs: &T) -> ExecutionResult { + Ok(false) + } + + fn kind_mismatch( + &mut self, + lhs: &L, + rhs: &R, + ) -> ExecutionResult { + let path_str = PathSegment::fmt_path(&self.path); + Err(self.error_span.type_error(format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs.articled_kind(), + path_str, + rhs.articled_kind() + ))) + } + + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Self::Result { + let path_str = PathSegment::fmt_path(&self.path); + Err(self.error_span.type_error(format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs.articled_display_name(), + path_str, + rhs.articled_display_name() + ))) + } + + #[inline] + fn lengths_unequal( + &mut self, + _lhs_len: Option, + _rhs_len: Option, + ) -> ExecutionResult { + Ok(false) + } + + #[inline] + fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> ExecutionResult { + Ok(false) + } + + #[inline] + fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::ArrayIndex(index)); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::ObjectKey(key.to_string())); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::IteratorIndex(index)); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::RangeStart); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::RangeEnd); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn should_short_circuit(&self, result: &ExecutionResult) -> bool { + // Short-circuit on Ok(false) or Err(_) + !matches!(result, Ok(true)) + } +} + +// ============================================================================ +// Debug Equality - Captures detailed information about equality failures +// ============================================================================ + +/// Which side of the comparison is missing a key. +#[derive(Debug, Clone, Copy)] +pub(crate) enum MissingSide { + #[allow(dead_code)] + Lhs, + Rhs, +} + +/// The reason why two values were not equal. +#[derive(Debug, Clone)] +pub(crate) enum DebugInequalityReason { + /// Values of the same type have different values. + ValueMismatch { + lhs_display: String, + rhs_display: String, + }, + /// Values have incompatible value kinds. + ValueKindMismatch { + lhs_kind: ValueKind, + rhs_kind: ValueKind, + }, + /// Ranges have incompatible structures. + RangeStructureMismatch { + lhs_structure: RangeStructure, + rhs_structure: RangeStructure, + }, + /// Collections have different lengths. + LengthMismatch { + lhs_len: Option, + rhs_len: Option, + }, + /// Object is missing a key on one side. + MissingKey { + key: String, + missing_on: MissingSide, + }, +} + +pub(crate) struct DebugEqualityError { + inner: Box, +} + +struct DebugEqualityErrorInner { + path: Vec, + reason: DebugInequalityReason, +} + +impl DebugEqualityError { + pub fn format_message(&self) -> String { + let inner = &self.inner; + let path_str = PathSegment::fmt_path(&inner.path); + + match &inner.reason { + DebugInequalityReason::ValueMismatch { + lhs_display, + rhs_display, + } => { + format!( + "lhs{} != rhs{}: {} != {}", + path_str, path_str, lhs_display, rhs_display + ) + } + DebugInequalityReason::ValueKindMismatch { lhs_kind, rhs_kind } => { + format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs_kind.articled_display_name(), + path_str, + rhs_kind.articled_display_name() + ) + } + DebugInequalityReason::RangeStructureMismatch { + lhs_structure: lhs_kind, + rhs_structure: rhs_kind, + } => { + format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs_kind.articled_display_name(), + path_str, + rhs_kind.articled_display_name() + ) + } + DebugInequalityReason::LengthMismatch { lhs_len, rhs_len } => { + format!( + "lhs{} has length {}, but rhs{} has length {}", + path_str, + lhs_len.unwrap_or(0), + path_str, + rhs_len.unwrap_or(0) + ) + } + DebugInequalityReason::MissingKey { key, missing_on } => match missing_on { + MissingSide::Lhs => { + format!( + "lhs{} is missing key {:?}, compared to rhs{}", + path_str, key, path_str + ) + } + MissingSide::Rhs => { + format!( + "lhs{} has extra key {:?}, compared to rhs{}", + path_str, key, path_str + ) + } + }, + } + } +} + +impl From for DebugEqualityError { + fn from(inner: DebugEqualityErrorInner) -> Self { + Self { + inner: Box::new(inner), + } + } +} + +/// Debug equality context - captures detailed information about why values differ. +/// This is useful for assertion failures where you want to show exactly where +/// the mismatch occurred. +pub(crate) struct DebugEquality { + path: Vec, +} + +impl DebugEquality { + pub(crate) fn new() -> Self { + Self { path: Vec::new() } + } +} + +impl EqualityContext for DebugEquality { + type Result = Result<(), DebugEqualityError>; + + #[inline] + fn values_equal(&mut self) -> Result<(), DebugEqualityError> { + Ok(()) + } + + #[inline] + fn leaf_values_not_equal( + &mut self, + lhs: &T, + rhs: &T, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::ValueMismatch { + lhs_display: format!("{:?}", lhs), + rhs_display: format!("{:?}", rhs), + }, + })? + } + + fn kind_mismatch( + &mut self, + lhs: &L, + rhs: &R, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::ValueKindMismatch { + lhs_kind: lhs.value_kind(), + rhs_kind: rhs.value_kind(), + }, + })? + } + + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::RangeStructureMismatch { + lhs_structure: lhs, + rhs_structure: rhs, + }, + })? + } + + #[inline] + fn lengths_unequal( + &mut self, + lhs_len: Option, + rhs_len: Option, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::LengthMismatch { lhs_len, rhs_len }, + })? + } + + #[inline] + fn missing_key( + &mut self, + key: &str, + missing_on: MissingSide, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::MissingKey { + key: key.to_string(), + missing_on, + }, + })? + } + + #[inline] + fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::ArrayIndex(index)); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::ObjectKey(key.to_string())); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::IteratorIndex(index)); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::RangeStart); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::RangeEnd); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn should_short_circuit(&self, result: &Result<(), DebugEqualityError>) -> bool { + result.is_err() + } +} + +// ============================================================================ +// ValuesEqual trait - Value equality with configurable context +// ============================================================================ + +/// A trait for comparing values for equality with preinterpret semantics. +/// +/// This is NOT the same as Rust's `PartialEq`/`Eq` traits because: +/// - **Type coercion**: Untyped integers/floats can equal typed ones (e.g., `5 == 5u32`) +/// - **Structural comparison**: Arrays, objects, and iterators are compared element-wise +/// - **Token comparison**: Streams and unsupported literals compare via token string representation +/// - **Float semantics**: Floats use Rust's `==`, so `NaN != NaN` +/// +/// The comparison behavior is controlled by the `EqualityContext`: +/// - `SimpleEquality`: Returns `false` on type mismatch (like JS `===`) +/// - `TypedEquality`: Errors on type mismatch with path information +/// - `DebugEquality`: Returns detailed error info for assertion messages +pub(crate) trait ValuesEqual: Sized + HasValueKind { + /// Compare two values for equality using the given context. + fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result; + + /// Lenient equality - returns `false` for incompatible types instead of erroring. + /// Behaves like JavaScript's `===` operator. + fn lenient_eq(&self, other: &Self) -> bool { + self.test_equality(other, &mut SimpleEquality) + } + + /// Strict equality check that errors on incompatible types. + fn typed_eq(&self, other: &Self, error_span: SpanRange) -> ExecutionResult { + self.test_equality(other, &mut TypedEquality::new(error_span)) + } + + /// Debug equality check - returns detailed information about why values differ. + /// Useful for generating informative assertion failure messages. + fn debug_eq(&self, other: &Self) -> Result<(), DebugEqualityError> { + self.test_equality(other, &mut DebugEquality::new()) + } +} diff --git a/src/expressions/mod.rs b/src/expressions/mod.rs index a2590409..e24690e4 100644 --- a/src/expressions/mod.rs +++ b/src/expressions/mod.rs @@ -4,6 +4,7 @@ use expression_parsing::*; mod concepts; mod control_flow; +mod equality; mod evaluation; mod expression; mod expression_block; @@ -18,6 +19,7 @@ mod values; #[allow(unused_imports)] // Whilst we're building it out pub(crate) use concepts::*; pub(crate) use control_flow::*; +pub(crate) use equality::*; pub(crate) use evaluation::*; pub(crate) use expression::*; pub(crate) use expression_block::*; diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index 7ce286e7..ca6e82e5 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -114,9 +114,11 @@ pub(crate) enum CastTarget { impl CastTarget { fn from_source_type(s: TypeIdent) -> Option { Some(match s.kind { - TypeKind::Parent(ParentTypeKind::Integer) => CastTarget::Integer(IntegerKind::Untyped), + TypeKind::Parent(ParentTypeKind::Integer(_)) => { + CastTarget::Integer(IntegerKind::Untyped) + } TypeKind::Leaf(ValueKind::Integer(kind)) => CastTarget::Integer(kind), - TypeKind::Parent(ParentTypeKind::Float) => CastTarget::Float(FloatKind::Untyped), + TypeKind::Parent(ParentTypeKind::Float(_)) => CastTarget::Float(FloatKind::Untyped), TypeKind::Leaf(ValueKind::Float(kind)) => CastTarget::Float(kind), TypeKind::Leaf(ValueKind::Boolean) => CastTarget::Boolean, TypeKind::Leaf(ValueKind::String) => CastTarget::String, diff --git a/src/expressions/type_resolution/value_kinds.rs b/src/expressions/type_resolution/value_kinds.rs index 7099881e..b04eb0f5 100644 --- a/src/expressions/type_resolution/value_kinds.rs +++ b/src/expressions/type_resolution/value_kinds.rs @@ -338,34 +338,34 @@ impl TypeKind { } pub(crate) enum ParentTypeKind { - Value, - Integer, - Float, + Value(ValueTypeKind), + Integer(IntegerTypeKind), + Float(FloatTypeKind), } impl ParentTypeKind { pub(crate) fn from_source_name(name: &str) -> Option { Some(match name { - "value" => ParentTypeKind::Value, - "int" => ParentTypeKind::Integer, - "float" => ParentTypeKind::Float, + ValueTypeKind::SOURCE_TYPE_NAME => ParentTypeKind::Value(ValueTypeKind), + IntegerTypeKind::SOURCE_TYPE_NAME => ParentTypeKind::Integer(IntegerTypeKind), + FloatTypeKind::SOURCE_TYPE_NAME => ParentTypeKind::Float(FloatTypeKind), _ => return None, }) } pub(crate) fn source_name(&self) -> &'static str { match self { - ParentTypeKind::Value => "value", - ParentTypeKind::Integer => "int", - ParentTypeKind::Float => "float", + ParentTypeKind::Value(ValueTypeKind) => ValueTypeKind::SOURCE_TYPE_NAME, + ParentTypeKind::Integer(IntegerTypeKind) => IntegerTypeKind::SOURCE_TYPE_NAME, + ParentTypeKind::Float(FloatTypeKind) => FloatTypeKind::SOURCE_TYPE_NAME, } } - pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { + pub(crate) fn method_resolver(&self) -> &'static dyn MethodResolver { match self { - ParentTypeKind::Value => &ValueTypeData, - ParentTypeKind::Integer => &IntegerTypeData, - ParentTypeKind::Float => &FloatTypeData, + ParentTypeKind::Value(x) => x.method_resolver(), + ParentTypeKind::Integer(x) => x.method_resolver(), + ParentTypeKind::Float(x) => x.method_resolver(), } } } diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 2c2d949a..2a9beac5 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) ArrayType => ValueType(ValueContent::Array), + content: ArrayValue, + kind: pub(crate) ArrayKind, + type_name: "array", + articled_display_name: "an array", + temp_type_data: ArrayTypeData, +} + #[derive(Clone)] pub(crate) struct ArrayValue { pub(crate) items: Vec, diff --git a/src/expressions/values/boolean.rs b/src/expressions/values/boolean.rs index 14adef3d..1601f878 100644 --- a/src/expressions/values/boolean.rs +++ b/src/expressions/values/boolean.rs @@ -2,6 +2,15 @@ use super::*; +define_leaf_type! { + pub(crate) BoolType => ValueType(ValueContent::Bool), + content: bool, + kind: pub(crate) BoolKind, + type_name: "bool", + articled_display_name: "a boolean", + temp_type_data: BooleanTypeData, +} + #[derive(Clone)] pub(crate) struct BooleanValue { pub(crate) value: bool, diff --git a/src/expressions/values/character.rs b/src/expressions/values/character.rs index a89ee26f..8f63373d 100644 --- a/src/expressions/values/character.rs +++ b/src/expressions/values/character.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) CharType => ValueType(ValueContent::Char), + content: char, + kind: pub(crate) CharKind, + type_name: "char", + articled_display_name: "a char", + temp_type_data: CharTypeData, +} + #[derive(Clone)] pub(crate) struct CharValue { pub(super) value: char, diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index 967f6f25..d61b47a2 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -1,5 +1,20 @@ use super::*; +define_parent_type! { + pub(crate) FloatType => ValueType(ValueContent::Float), + content: pub(crate) FloatContent, + leaf_kind: pub(crate) FloatLeafKind, + type_kind: ParentTypeKind::Float(pub(crate) FloatTypeKind), + variants: { + Untyped => UntypedFloatType, + F32 => F32Type, + F64 => F64Type, + }, + type_name: "float", + articled_display_name: "a float", + temp_type_data: FloatTypeData, +} + #[derive(Copy, Clone)] pub(crate) enum FloatValue { Untyped(UntypedFloat), diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 597db858..372fbaee 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -164,7 +164,16 @@ macro_rules! impl_float_operations { impl_float_operations!(F32TypeData mod f32_interface: F32(f32), F64TypeData mod f64_interface: F64(f64)); macro_rules! impl_resolvable_float_subtype { - ($value_type:ty, $type:ty, $variant:ident, $expected_msg:expr) => { + ($type_def:ident, $kind:ident, $value_type:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { + define_leaf_type! { + pub(crate) $type_def => FloatType(FloatContent::$variant) => ValueType, + content: $type, + kind: pub(crate) $kind, + type_name: $type_name, + articled_display_name: $articled_display_name, + temp_type_data: $value_type, + } + impl ResolvableArgumentTarget for $type { type ValueType = $value_type; } @@ -183,7 +192,7 @@ macro_rules! impl_resolvable_float_subtype { match value { FloatValue::Untyped(x) => Ok(x.into_fallback() as $type), FloatValue::$variant(x) => Ok(x), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } @@ -195,7 +204,7 @@ macro_rules! impl_resolvable_float_subtype { ) -> ExecutionResult { match value { Value::Float(x) => <$type>::resolve_from_value(x, context), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } @@ -207,7 +216,7 @@ macro_rules! impl_resolvable_float_subtype { ) -> ExecutionResult<&'a Self> { match value { Value::Float(FloatValue::$variant(x)) => Ok(x), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } @@ -219,12 +228,12 @@ macro_rules! impl_resolvable_float_subtype { ) -> ExecutionResult<&'a mut Self> { match value { Value::Float(FloatValue::$variant(x)) => Ok(x), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } }; } -impl_resolvable_float_subtype!(F32TypeData, f32, F32, "an f32"); -impl_resolvable_float_subtype!(F64TypeData, f64, F64, "an f64"); +impl_resolvable_float_subtype!(F32Type, F32Kind, F32TypeData, f32, F32, "f32", "an f32"); +impl_resolvable_float_subtype!(F64Type, F64Kind, F64TypeData, f64, F64, "f64", "an f64"); diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 43ab7d00..fe3ee8bf 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) UntypedFloatType => FloatType(FloatContent::Untyped) => ValueType, + content: UntypedFloat, + kind: pub(crate) UntypedFloatKind, + type_name: "untyped_float", + articled_display_name: "an untyped float", + temp_type_data: UntypedFloatTypeData, +} + #[derive(Copy, Clone)] pub(crate) struct UntypedFloat(FallbackFloat); pub(crate) type FallbackFloat = f64; diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index 80274fe3..33de206f 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -1,5 +1,30 @@ use super::*; +define_parent_type! { + pub(crate) IntegerType => ValueType(ValueContent::Integer), + content: pub(crate) IntegerContent, + leaf_kind: pub(crate) IntegerLeafKind, + type_kind: ParentTypeKind::Integer(pub(crate) IntegerTypeKind), + variants: { + Untyped => UntypedIntegerType, + U8 => U8Type, + U16 => U16Type, + U32 => U32Type, + U64 => U64Type, + U128 => U128Type, + Usize => UsizeType, + I8 => I8Type, + I16 => I16Type, + I32 => I32Type, + I64 => I64Type, + I128 => I128Type, + Isize => IsizeType, + }, + type_name: "int", + articled_display_name: "an integer", + temp_type_data: IntegerTypeData, +} + #[derive(Copy, Clone)] pub(crate) enum IntegerValue { Untyped(UntypedInteger), diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 71b93d35..beca4729 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -197,7 +197,16 @@ impl_int_operations!( ); macro_rules! impl_resolvable_integer_subtype { - ($value_type:ty, $type:ty, $variant:ident, $expected_msg:expr) => { + ($type_def:ident, $kind:ident, $value_type:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { + define_leaf_type! { + pub(crate) $type_def => IntegerType(IntegerContent::$variant) => ValueType, + content: $type, + kind: pub(crate) $kind, + type_name: $type_name, + articled_display_name: $articled_display_name, + temp_type_data: $value_type, + } + impl ResolvableArgumentTarget for $type { type ValueType = $value_type; } @@ -216,7 +225,7 @@ macro_rules! impl_resolvable_integer_subtype { match value { IntegerValue::Untyped(x) => Ok(x.into_fallback() as $type), IntegerValue::$variant(x) => Ok(x), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } @@ -228,7 +237,7 @@ macro_rules! impl_resolvable_integer_subtype { ) -> ExecutionResult { match value { Value::Integer(x) => <$type>::resolve_from_value(x, context), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } @@ -240,7 +249,7 @@ macro_rules! impl_resolvable_integer_subtype { ) -> ExecutionResult<&'a Self> { match value { Value::Integer(IntegerValue::$variant(x)) => Ok(x), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } @@ -252,22 +261,54 @@ macro_rules! impl_resolvable_integer_subtype { ) -> ExecutionResult<&'a mut Self> { match value { Value::Integer(IntegerValue::$variant(x)) => Ok(x), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } }; } -impl_resolvable_integer_subtype!(I8TypeData, i8, I8, "an i8"); -impl_resolvable_integer_subtype!(I16TypeData, i16, I16, "an i16"); -impl_resolvable_integer_subtype!(I32TypeData, i32, I32, "an i32"); -impl_resolvable_integer_subtype!(I64TypeData, i64, I64, "an i64"); -impl_resolvable_integer_subtype!(I128TypeData, i128, I128, "an i128"); -impl_resolvable_integer_subtype!(IsizeTypeData, isize, Isize, "an isize"); -impl_resolvable_integer_subtype!(U8TypeData, u8, U8, "a u8"); -impl_resolvable_integer_subtype!(U16TypeData, u16, U16, "a u16"); -impl_resolvable_integer_subtype!(U32TypeData, u32, U32, "a u32"); -impl_resolvable_integer_subtype!(U64TypeData, u64, U64, "a u64"); -impl_resolvable_integer_subtype!(U128TypeData, u128, U128, "a u128"); -impl_resolvable_integer_subtype!(UsizeTypeData, usize, Usize, "a usize"); +impl_resolvable_integer_subtype!(I8Type, I8Kind, I8TypeData, i8, I8, "i8", "an i8"); +impl_resolvable_integer_subtype!(I16Type, I16Kind, I16TypeData, i16, I16, "i16", "an i16"); +impl_resolvable_integer_subtype!(I32Type, I32Kind, I32TypeData, i32, I32, "i32", "an i32"); +impl_resolvable_integer_subtype!(I64Type, I64Kind, I64TypeData, i64, I64, "i64", "an i64"); +impl_resolvable_integer_subtype!( + I128Type, + I128Kind, + I128TypeData, + i128, + I128, + "i128", + "an i128" +); +impl_resolvable_integer_subtype!( + IsizeType, + IsizeKind, + IsizeTypeData, + isize, + Isize, + "isize", + "an isize" +); +impl_resolvable_integer_subtype!(U8Type, U8Kind, U8TypeData, u8, U8, "u8", "a u8"); +impl_resolvable_integer_subtype!(U16Type, U16Kind, U16TypeData, u16, U16, "u16", "a u16"); +impl_resolvable_integer_subtype!(U32Type, U32Kind, U32TypeData, u32, U32, "u32", "a u32"); +impl_resolvable_integer_subtype!(U64Type, U64Kind, U64TypeData, u64, U64, "u64", "a u64"); +impl_resolvable_integer_subtype!( + U128Type, + U128Kind, + U128TypeData, + u128, + U128, + "u128", + "a u128" +); +impl_resolvable_integer_subtype!( + UsizeType, + UsizeKind, + UsizeTypeData, + usize, + Usize, + "usize", + "a usize" +); diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index fe4fcfd6..71567bb3 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) UntypedIntegerType => IntegerType(IntegerContent::Untyped) => ValueType, + content: UntypedInteger, + kind: pub(crate) UntypedIntegerKind, + type_name: "untyped_int", + articled_display_name: "an untyped integer", + temp_type_data: UntypedIntegerTypeData, +} + #[derive(Copy, Clone)] pub(crate) struct UntypedInteger(FallbackInteger); pub(crate) type FallbackInteger = i128; diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index 917d32cb..e6b4f765 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) IteratorType => ValueType(ValueContent::Iterator), + content: IteratorValue, + kind: pub(crate) IteratorKind, + type_name: "iterator", + articled_display_name: "an iterator", + temp_type_data: IteratorTypeData, +} + #[derive(Clone)] pub(crate) struct IteratorValue { iterator: IteratorValueInner, diff --git a/src/expressions/values/none.rs b/src/expressions/values/none.rs index 569b6a65..e22b12cf 100644 --- a/src/expressions/values/none.rs +++ b/src/expressions/values/none.rs @@ -1,5 +1,15 @@ use super::*; +define_leaf_type! { + pub(crate) NoneType => ValueType(ValueContent::None), + content: (), + kind: pub(crate) NoneKind, + type_name: "none", + // Instead of saying "expected a none value", we can say "expected None" + articled_display_name: "None", + temp_type_data: NoneTypeData, +} + impl IntoValue for () { fn into_value(self) -> Value { Value::None diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index a464d8d0..279a2df7 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) ObjectType => ValueType(ValueContent::Object), + content: ObjectValue, + kind: pub(crate) ObjectKind, + type_name: "object", + articled_display_name: "an object", + temp_type_data: ObjectTypeData, +} + #[derive(Clone)] pub(crate) struct ObjectValue { pub(crate) entries: BTreeMap, diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index aa3416d9..8434d1c9 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) ParserType => ValueType(ValueContent::Parser), + content: ParserHandle, + kind: pub(crate) ParserKind, + type_name: "parser", + articled_display_name: "a parser", + temp_type_data: ParserTypeData, +} + #[derive(Clone)] pub(crate) struct ParserValue { handle: ParserHandle, diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index 7681e2a3..c343dfe6 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -2,6 +2,15 @@ use syn::RangeLimits; use super::*; +define_leaf_type! { + pub(crate) RangeType => ValueType(ValueContent::Range), + content: RangeValue, + kind: pub(crate) RangeKind, + type_name: "range", + articled_display_name: "a range", + temp_type_data: RangeTypeData, +} + #[derive(Clone)] pub(crate) struct RangeValue { pub(crate) inner: Box, diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index 1fe5f711..f3f9c0ae 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) StreamType => ValueType(ValueContent::Stream), + content: StreamValue, + kind: pub(crate) StreamKind, + type_name: "stream", + articled_display_name: "a stream", + temp_type_data: StreamTypeData, +} + #[derive(Clone)] pub(crate) struct StreamValue { pub(crate) value: OutputStream, diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index fee4eeb3..7697051e 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) StringType => ValueType(ValueContent::String), + content: String, + kind: pub(crate) StringKind, + type_name: "string", + articled_display_name: "a string", + temp_type_data: StringTypeData, +} + #[derive(Clone)] pub(crate) struct StringValue { pub(crate) value: String, diff --git a/src/expressions/values/unsupported_literal.rs b/src/expressions/values/unsupported_literal.rs index d4fc7d80..c3ed0958 100644 --- a/src/expressions/values/unsupported_literal.rs +++ b/src/expressions/values/unsupported_literal.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) UnsupportedLiteralType => ValueType(ValueContent::UnsupportedLiteral), + content: syn::Lit, + kind: pub(crate) UnsupportedLiteralKind, + type_name: "unsupported_literal", + articled_display_name: "an unsupported literal", + temp_type_data: UnsupportedLiteralTypeData, +} + #[derive(Clone)] pub(crate) struct UnsupportedLiteral { pub(crate) lit: syn::Lit, diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index ed9838b9..2f00d227 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -1,581 +1,42 @@ use super::*; -// ============================================================================ -// Equality Context - Controls behavior of value equality comparisons -// ============================================================================ - -/// A segment in the path to the current comparison location. -#[derive(Clone, Debug)] -pub(crate) enum PathSegment { - ArrayIndex(usize), - ObjectKey(String), - IteratorIndex(usize), - RangeStart, - RangeEnd, -} - -impl PathSegment { - fn fmt_path(path: &[PathSegment]) -> String { - let mut result = String::new(); - for segment in path { - match segment { - PathSegment::ArrayIndex(i) => result.push_str(&format!("[{}]", i)), - PathSegment::ObjectKey(k) => { - if syn::parse_str::(k).is_ok() { - result.push_str(&format!(".{}", k)) - } else { - result.push_str(&format!("[{:?}]", k)) - } - } - PathSegment::IteratorIndex(i) => result.push_str(&format!("[{}]", i)), - PathSegment::RangeStart => result.push_str(".start"), - PathSegment::RangeEnd => result.push_str(".end"), - } - } - result - } -} - -/// Context trait for controlling equality comparison behavior. -/// -/// Different implementations allow for: -/// - Simple equality: returns `false` on type mismatch (like JS `===`) -/// - Typed equality: errors on type mismatch, tracks path for error messages -/// - Assert equality: errors on any difference, useful for assert_eq -pub(crate) trait EqualityContext { - /// The result type returned by equality comparisons. - type Result; - - /// Values are equal. - fn values_equal(&mut self) -> Self::Result; - - /// Values of the same type are not equal. - fn leaf_values_not_equal(&mut self, lhs: &T, rhs: &T) -> Self::Result; - - /// Values have different kinds. - fn kind_mismatch(&mut self, lhs: &L, rhs: &R) - -> Self::Result; - - /// Range values have different structures. - fn range_structure_mismatch( - &mut self, - lhs: RangeStructure, - rhs: RangeStructure, - ) -> Self::Result; - - /// Arrays or iterators have different lengths. - fn lengths_unequal(&mut self, lhs_len: Option, rhs_len: Option) -> Self::Result; - - /// Object is missing a key that the other has. - fn missing_key(&mut self, key: &str, missing_on: MissingSide) -> Self::Result; - - fn iteration_limit_exceeded(&mut self, limit: usize) -> Self::Result { - let message = format!("iteration limit {} exceeded", limit); - self.leaf_values_not_equal(&message, &message) - } - - /// Wrap a comparison within an array index context. - fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R; - - /// Wrap a comparison within an object key context. - fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R; - - /// Wrap a comparison within an iterator index context. - fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R; - - /// Wrap a comparison within a range start context. - fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R; - - /// Wrap a comparison within a range end context. - fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R; - - /// Returns true if the result indicates we should stop comparing and return early. - /// This is true when the result indicates "not equal" or an error occurred. - fn should_short_circuit(&self, result: &Self::Result) -> bool; -} - -/// Simple equality context - returns `false` on type mismatch, no path tracking. -/// This is the most efficient option when you just need a bool result. -pub(crate) struct SimpleEquality; - -impl EqualityContext for SimpleEquality { - type Result = bool; - - #[inline] - fn values_equal(&mut self) -> bool { - true - } - - #[inline] - fn leaf_values_not_equal(&mut self, _lhs: &T, _rhs: &T) -> bool { - false - } - - #[inline] - fn kind_mismatch(&mut self, _lhs: &L, _rhs: &R) -> bool { - false - } - - #[inline] - fn range_structure_mismatch(&mut self, _lhs: RangeStructure, _rhs: RangeStructure) -> bool { - false - } - - #[inline] - fn lengths_unequal(&mut self, _lhs_len: Option, _rhs_len: Option) -> bool { - false - } - - #[inline] - fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> bool { - false - } - - #[inline] - fn with_array_index(&mut self, _index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - f(self) - } - - #[inline] - fn with_object_key(&mut self, _key: &str, f: impl FnOnce(&mut Self) -> R) -> R { - f(self) - } - - #[inline] - fn with_iterator_index(&mut self, _index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - f(self) - } - - #[inline] - fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - f(self) - } - - #[inline] - fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - f(self) - } - - #[inline] - fn should_short_circuit(&self, result: &bool) -> bool { - !*result - } -} - -/// Typed equality context - errors on type mismatch, tracks path for error messages. -pub(crate) struct TypedEquality { - path: Vec, - error_span: SpanRange, -} - -impl TypedEquality { - pub(crate) fn new(error_span: SpanRange) -> Self { - Self { - path: Vec::new(), - error_span, - } - } -} - -impl EqualityContext for TypedEquality { - type Result = ExecutionResult; - - #[inline] - fn values_equal(&mut self) -> ExecutionResult { - Ok(true) - } - - #[inline] - fn leaf_values_not_equal(&mut self, _lhs: &T, _rhs: &T) -> ExecutionResult { - Ok(false) - } - - fn kind_mismatch( - &mut self, - lhs: &L, - rhs: &R, - ) -> ExecutionResult { - let path_str = PathSegment::fmt_path(&self.path); - Err(self.error_span.type_error(format!( - "lhs{} is {}, but rhs{} is {}", - path_str, - lhs.articled_kind(), - path_str, - rhs.articled_kind() - ))) - } - - fn range_structure_mismatch( - &mut self, - lhs: RangeStructure, - rhs: RangeStructure, - ) -> Self::Result { - let path_str = PathSegment::fmt_path(&self.path); - Err(self.error_span.type_error(format!( - "lhs{} is {}, but rhs{} is {}", - path_str, - lhs.articled_display_name(), - path_str, - rhs.articled_display_name() - ))) - } - - #[inline] - fn lengths_unequal( - &mut self, - _lhs_len: Option, - _rhs_len: Option, - ) -> ExecutionResult { - Ok(false) - } - - #[inline] - fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> ExecutionResult { - Ok(false) - } - - #[inline] - fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::ArrayIndex(index)); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::ObjectKey(key.to_string())); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::IteratorIndex(index)); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::RangeStart); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::RangeEnd); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn should_short_circuit(&self, result: &ExecutionResult) -> bool { - // Short-circuit on Ok(false) or Err(_) - !matches!(result, Ok(true)) - } -} - -// ============================================================================ -// Debug Equality - Captures detailed information about equality failures -// ============================================================================ - -/// Which side of the comparison is missing a key. -#[derive(Debug, Clone, Copy)] -pub(crate) enum MissingSide { - #[allow(dead_code)] - Lhs, - Rhs, -} - -/// The reason why two values were not equal. -#[derive(Debug, Clone)] -pub(crate) enum DebugInequalityReason { - /// Values of the same type have different values. - ValueMismatch { - lhs_display: String, - rhs_display: String, +impl From for ValueKind { + fn from(_kind: ValueLeafKind) -> Self { + unimplemented!() + } +} + +// TODO[concepts]: Uncomment when ready +// pub(crate) type QqqValue = Actual<'static, ValueType, BeOwned>; +// pub(crate) type QqqValueReferencable = Actual<'static, ValueType, BeReferenceable>; +// pub(crate) type QqqValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; +// pub(crate) type QqqValueMut<'a> = Actual<'a, ValueType, BeAnyMut>; + +define_parent_type! { + pub(crate) ValueType, + content: pub(crate) ValueContent, + leaf_kind: pub(crate) ValueLeafKind, + type_kind: ParentTypeKind::Value(pub(crate) ValueTypeKind), + variants: { + None => NoneType, + Integer => IntegerType, + Float => FloatType, + Bool => BoolType, + String => StringType, + Char => CharType, + // Unsupported literal is a type here so that we can parse such a token + // as a value rather than a stream, and give it better error messages + UnsupportedLiteral => UnsupportedLiteralType, + Array => ArrayType, + Object => ObjectType, + Stream => StreamType, + Range => RangeType, + Iterator => IteratorType, + Parser => ParserType, }, - /// Values have incompatible value kinds. - ValueKindMismatch { - lhs_kind: ValueKind, - rhs_kind: ValueKind, - }, - /// Ranges have incompatible structures. - RangeStructureMismatch { - lhs_structure: RangeStructure, - rhs_structure: RangeStructure, - }, - /// Collections have different lengths. - LengthMismatch { - lhs_len: Option, - rhs_len: Option, - }, - /// Object is missing a key on one side. - MissingKey { - key: String, - missing_on: MissingSide, - }, -} - -pub(crate) struct DebugEqualityError { - inner: Box, -} - -struct DebugEqualityErrorInner { - path: Vec, - reason: DebugInequalityReason, -} - -impl DebugEqualityError { - pub fn format_message(&self) -> String { - let inner = &self.inner; - let path_str = PathSegment::fmt_path(&inner.path); - - match &inner.reason { - DebugInequalityReason::ValueMismatch { - lhs_display, - rhs_display, - } => { - format!( - "lhs{} != rhs{}: {} != {}", - path_str, path_str, lhs_display, rhs_display - ) - } - DebugInequalityReason::ValueKindMismatch { lhs_kind, rhs_kind } => { - format!( - "lhs{} is {}, but rhs{} is {}", - path_str, - lhs_kind.articled_display_name(), - path_str, - rhs_kind.articled_display_name() - ) - } - DebugInequalityReason::RangeStructureMismatch { - lhs_structure: lhs_kind, - rhs_structure: rhs_kind, - } => { - format!( - "lhs{} is {}, but rhs{} is {}", - path_str, - lhs_kind.articled_display_name(), - path_str, - rhs_kind.articled_display_name() - ) - } - DebugInequalityReason::LengthMismatch { lhs_len, rhs_len } => { - format!( - "lhs{} has length {}, but rhs{} has length {}", - path_str, - lhs_len.unwrap_or(0), - path_str, - rhs_len.unwrap_or(0) - ) - } - DebugInequalityReason::MissingKey { key, missing_on } => match missing_on { - MissingSide::Lhs => { - format!( - "lhs{} is missing key {:?}, compared to rhs{}", - path_str, key, path_str - ) - } - MissingSide::Rhs => { - format!( - "lhs{} has extra key {:?}, compared to rhs{}", - path_str, key, path_str - ) - } - }, - } - } -} - -impl From for DebugEqualityError { - fn from(inner: DebugEqualityErrorInner) -> Self { - Self { - inner: Box::new(inner), - } - } -} - -/// Debug equality context - captures detailed information about why values differ. -/// This is useful for assertion failures where you want to show exactly where -/// the mismatch occurred. -pub(crate) struct DebugEquality { - path: Vec, -} - -impl DebugEquality { - pub(crate) fn new() -> Self { - Self { path: Vec::new() } - } -} - -impl EqualityContext for DebugEquality { - type Result = Result<(), DebugEqualityError>; - - #[inline] - fn values_equal(&mut self) -> Result<(), DebugEqualityError> { - Ok(()) - } - - #[inline] - fn leaf_values_not_equal( - &mut self, - lhs: &T, - rhs: &T, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::ValueMismatch { - lhs_display: format!("{:?}", lhs), - rhs_display: format!("{:?}", rhs), - }, - })? - } - - fn kind_mismatch( - &mut self, - lhs: &L, - rhs: &R, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::ValueKindMismatch { - lhs_kind: lhs.value_kind(), - rhs_kind: rhs.value_kind(), - }, - })? - } - - fn range_structure_mismatch( - &mut self, - lhs: RangeStructure, - rhs: RangeStructure, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::RangeStructureMismatch { - lhs_structure: lhs, - rhs_structure: rhs, - }, - })? - } - - #[inline] - fn lengths_unequal( - &mut self, - lhs_len: Option, - rhs_len: Option, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::LengthMismatch { lhs_len, rhs_len }, - })? - } - - #[inline] - fn missing_key( - &mut self, - key: &str, - missing_on: MissingSide, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::MissingKey { - key: key.to_string(), - missing_on, - }, - })? - } - - #[inline] - fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::ArrayIndex(index)); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::ObjectKey(key.to_string())); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::IteratorIndex(index)); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::RangeStart); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::RangeEnd); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn should_short_circuit(&self, result: &Result<(), DebugEqualityError>) -> bool { - result.is_err() - } -} - -// ============================================================================ -// ValuesEqual trait - Value equality with configurable context -// ============================================================================ - -/// A trait for comparing values for equality with preinterpret semantics. -/// -/// This is NOT the same as Rust's `PartialEq`/`Eq` traits because: -/// - **Type coercion**: Untyped integers/floats can equal typed ones (e.g., `5 == 5u32`) -/// - **Structural comparison**: Arrays, objects, and iterators are compared element-wise -/// - **Token comparison**: Streams and unsupported literals compare via token string representation -/// - **Float semantics**: Floats use Rust's `==`, so `NaN != NaN` -/// -/// The comparison behavior is controlled by the `EqualityContext`: -/// - `SimpleEquality`: Returns `false` on type mismatch (like JS `===`) -/// - `TypedEquality`: Errors on type mismatch with path information -/// - `DebugEquality`: Returns detailed error info for assertion messages -pub(crate) trait ValuesEqual: Sized + HasValueKind { - /// Compare two values for equality using the given context. - fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result; - - /// Lenient equality - returns `false` for incompatible types instead of erroring. - /// Behaves like JavaScript's `===` operator. - fn lenient_eq(&self, other: &Self) -> bool { - self.test_equality(other, &mut SimpleEquality) - } - - /// Strict equality check that errors on incompatible types. - fn typed_eq(&self, other: &Self, error_span: SpanRange) -> ExecutionResult { - self.test_equality(other, &mut TypedEquality::new(error_span)) - } - - /// Debug equality check - returns detailed information about why values differ. - /// Useful for generating informative assertion failure messages. - fn debug_eq(&self, other: &Self) -> Result<(), DebugEqualityError> { - self.test_equality(other, &mut DebugEquality::new()) - } + type_name: "value", + articled_display_name: "any value", + temp_type_data: ValueTypeData, } #[derive(Clone)] From f7737ddc129453d4f8664cf22a173243640b1f5f Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 3 Jan 2026 02:10:57 +0100 Subject: [PATCH 09/58] feat: Generated types are used for value kinds --- plans/TODO.md | 25 +- src/expressions/concepts/actual.rs | 15 +- src/expressions/concepts/type_traits.rs | 118 +++++-- src/expressions/equality.rs | 23 +- src/expressions/evaluation/value_frames.rs | 11 +- src/expressions/operations.rs | 75 ++-- src/expressions/type_resolution/arguments.rs | 2 +- .../type_resolution/value_kinds.rs | 334 +++--------------- src/expressions/values/array.rs | 8 - src/expressions/values/boolean.rs | 38 +- src/expressions/values/character.rs | 34 +- src/expressions/values/float.rs | 25 +- src/expressions/values/float_subtypes.rs | 40 +-- src/expressions/values/float_untyped.rs | 48 ++- src/expressions/values/integer.rs | 33 +- src/expressions/values/integer_subtypes.rs | 40 +-- src/expressions/values/integer_untyped.rs | 70 ++-- src/expressions/values/iterable.rs | 14 +- src/expressions/values/iterator.rs | 8 - src/expressions/values/object.rs | 8 - src/expressions/values/parser.rs | 8 +- src/expressions/values/range.rs | 8 - src/expressions/values/stream.rs | 10 +- src/expressions/values/string.rs | 8 +- src/expressions/values/unsupported_literal.rs | 8 +- src/expressions/values/value.rs | 41 +-- .../operations/compare_bool_and_int.stderr | 2 +- .../operations/logical_and_on_int.stderr | 2 +- .../operations/logical_or_on_int.stderr | 2 +- 29 files changed, 426 insertions(+), 632 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index ab5d23fa..6aae7a2e 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -210,24 +210,29 @@ First, read the @./2025-11-vision.md - [ ] Generate value kinds from the macros - [x] Add (temporary) ability to link to TypeData and resolve methods from there - [x] Then implement all the macros - - [ ] Add other methods to e.g. value kinds - i.e. Work out some way to _parse_ the value/type kinds - - [ ] And use that to generate ValueKind from the new macros + - [x] And use that to generate ValueLeafKind from the new macros + - [ ] Find a way to generate `from_source_name` - ideally efficiently - [ ] Add ability to implement IsIterable - [ ] `CastTarget` simply wraps `TypeKind` + - [ ] Create a `Ref` and a `Mut` form + - [ ] They won't implement `IsArgumentForm` + - [ ] Create suitably generic `as_ref()` and `as_mut()` methods on `Actual` + - [ ] Stage 1 of the form migration: + - [ ] Replace `Owned` as type reference to `Actual<..>` + - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` + - [ ] And similarly for other values... + - [ ] Stage 2 of the form migration: + - [ ] Migrate `Shared`, `Mutable`, `Assignee` - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): - - [ ] Owned - - [ ] Mutable, Owned => Mutable - - [ ] Shared, Owned => Shared - - [ ] Assignee, Mutable <=> Assignee - [ ] CopyOnWrite, Shared => CopyOnWrite x2, Owned => CopyOnWrite - [ ] LateBound, Tons of conversions into it - [ ] Argument, and `ArgumentOwnership` driven conversions into it - - [ ] Complete value definitions - - [ ] Strip wrapper types like `StreamValue` - can just use `OutputStream` as content - - [ ] Replace `Owned`, `Shared` etc as type references to `Actual<..>` + - [ ] Strip wrapper types like `StreamValue` - can just use `OutputStream` as content - [ ] Remove old definitions - [ ] Generate test over all value kinds which checks for: - [ ] source type has no spaces and is lower case, and is invertible + - [ ] Clear up all `TODO[concepts]` + - [ ] Have variables store a `Referenceable` ## Methods and closures @@ -524,7 +529,7 @@ Also: - [x] Merge `assignee_frames` into `value_frames` as per comment as the top of `assignee_frames` - [x] Rename `EvaluationItem` to `RequestedValue` and consider making `RequestedValue::AssignmentCompletion` wrap an `Owned<()>` so that it becomes truly a value. -- [x] Merge `HasValueType` with `ValueKind` +- [x] Merge `HasValueType` with `ValueLeafKind` - [ ] Add `preinterpret::macro` - can this be a declarative macro? Would be slightly more efficient, as it just needs to wrap a call to `preinterpret::stream` or `preinterpret::run`... - [ ] When we create `input = %raw[..]` we will need to set its `end_of_stream` span to the end of the macro_rules! macro somehow... I'm not sure how to get that span though. diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs index 5789c649..af96c5c3 100644 --- a/src/expressions/concepts/actual.rs +++ b/src/expressions/concepts/actual.rs @@ -30,9 +30,6 @@ impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> { self, ) -> Result, M::ShortCircuit<'a>> where - // TODO: See if we can move this bound lower down somehow? - // e.g. to IsHierarchicalType itself - // This may cause circular bound issues though, so let's do it in its own PR for<'l> T: IsHierarchicalType = >::Content<'l>>, F: IsHierarchicalForm, { @@ -78,6 +75,18 @@ impl<'a, T: IsType, F: IsFormOf> DerefMut for Actual<'a, T, F> { } } +impl<'a, T: IsHierarchicalType, F: IsFormOf> HasLeafKind for Actual<'a, T, F> +where + F: IsHierarchicalForm, + for<'l> T: IsHierarchicalType = >::Content<'l>>, +{ + type LeafKind = ::LeafKind; + + fn kind(&self) -> Self::LeafKind { + ::content_to_leaf_kind::(&self.0) + } +} + pub(crate) trait IsValueContent<'a> { type Type: IsType; type Form: IsFormOf; diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 06ee3657..4e7d3934 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -18,13 +18,24 @@ pub(crate) trait IsType: Sized { } pub(crate) trait IsHierarchicalType: IsType { - // >::Content<'a>> := Self::Content<'a, F> + // The following is always true, courtesy of the definition of IsFormOf: + // >::Content<'a>> := Self::Content<'a, F> + // So the following where clause can be added where needed to make types line up: + // for<'l> T: IsHierarchicalType = >::Content<'l>>, type Content<'a, F: IsHierarchicalForm>; type LeafKind: IsSpecificLeafKind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( structure: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; + + fn content_to_leaf_kind( + content: &Self::Content<'_, F>, + ) -> Self::LeafKind; +} + +pub(crate) trait IsLeafType: IsHierarchicalType { + fn leaf_kind() -> Self::LeafKind; } pub(crate) trait IsDynType: IsType { @@ -189,15 +200,11 @@ pub(crate) trait CastDyn { } pub(crate) trait HasLeafKind { - type LeafKindType: IsSpecificLeafKind; - - const KIND: Self::LeafKindType; + type LeafKind: IsSpecificLeafKind; - fn kind(&self) -> Self::LeafKindType { - Self::KIND - } + fn kind(&self) -> Self::LeafKind; - fn value_kind(&self) -> ValueKind { + fn value_kind(&self) -> ValueLeafKind { self.kind().into() } @@ -206,13 +213,22 @@ pub(crate) trait HasLeafKind { } } -impl> HasLeafKind for T -where - T::Type: HasLeafKind, -{ - type LeafKindType = ::LeafKindType; +// TODO[concepts]: Remove when we get rid of impl_resolvable_argument_for +impl HasLeafKind for &T { + type LeafKind = T::LeafKind; - const KIND: Self::LeafKindType = T::Type::KIND; + fn kind(&self) -> Self::LeafKind { + (**self).kind() + } +} + +// TODO[concepts]: Remove when we get rid of impl_resolvable_argument_for +impl HasLeafKind for &mut T { + type LeafKind = T::LeafKind; + + fn kind(&self) -> Self::LeafKind { + (**self).kind() + } } macro_rules! define_parent_type { @@ -276,12 +292,30 @@ macro_rules! define_parent_type { $( $content::$variant(x) => $content::$variant(x.map_with::()?), )* }) } + + fn content_to_leaf_kind( + content: &Self::Content<'_, F>, + ) -> Self::LeafKind { + content.kind() + } } $content_vis enum $content<'a, F: IsHierarchicalForm> { $( $variant(Actual<'a, $variant_type, F>), )* } + impl<'a, F: IsHierarchicalForm> HasLeafKind for $content<'a, F> { + type LeafKind = $leaf_kind; + + fn kind(&self) -> Self::LeafKind { + match self { + $($content::$variant(x) => $leaf_kind::$variant( + <$variant_type as IsHierarchicalType>::content_to_leaf_kind::(&x.0) + ),)* + } + } + } + #[derive(Clone, Copy, PartialEq, Eq)] $type_kind_vis struct $type_kind; @@ -299,15 +333,21 @@ macro_rules! define_parent_type { } $( - impl From<$leaf_kind> for ValueKind { + impl From<$leaf_kind> for ValueLeafKind { fn from(kind: $leaf_kind) -> Self { let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); - ValueKind::from(as_parent_kind) + ValueLeafKind::from(as_parent_kind) } } )? impl IsSpecificLeafKind for $leaf_kind { + fn source_type_name(&self) -> &'static str { + match self { + $( Self::$variant(x) => x.source_type_name(), )* + } + } + fn articled_display_name(&self) -> &'static str { match self { $( Self::$variant(x) => x.articled_display_name(), )* @@ -332,7 +372,7 @@ pub(crate) use define_parent_type; macro_rules! define_leaf_type { ( $type_def_vis:vis $type_def:ident => $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*, - content: $leaf_type:ty, + content: $content_type:ty, kind: $kind_vis:vis $kind:ident, type_name: $source_type_name:literal, articled_display_name: $articled_display_name:literal, @@ -347,18 +387,30 @@ macro_rules! define_leaf_type { const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; fn type_kind() -> TypeKind { - TypeKind::Leaf(ValueKind::from($kind)) + TypeKind::Leaf(ValueLeafKind::from($kind)) } } impl IsHierarchicalType for $type_def { - type Content<'a, F: IsHierarchicalForm> = F::Leaf<'a, $leaf_type>; + type Content<'a, F: IsHierarchicalForm> = F::Leaf<'a, $content_type>; type LeafKind = $kind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( content: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { - M::map_leaf::<$leaf_type>(content) + M::map_leaf::<$content_type>(content) + } + + fn content_to_leaf_kind( + _content: &Self::Content<'_, F>, + ) -> Self::LeafKind { + $kind + } + } + + impl IsLeafType for $type_def { + fn leaf_kind() -> $kind { + $kind } } @@ -389,14 +441,18 @@ macro_rules! define_leaf_type { #[derive(Clone, Copy, PartialEq, Eq)] $kind_vis struct $kind; - impl From<$kind> for ValueKind { + impl From<$kind> for ValueLeafKind { fn from(kind: $kind) -> Self { let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); - ValueKind::from(as_parent_kind) + ValueLeafKind::from(as_parent_kind) } } impl IsSpecificLeafKind for $kind { + fn source_type_name(&self) -> &'static str { + $source_type_name + } + fn articled_display_name(&self) -> &'static str { $articled_display_name } @@ -406,25 +462,33 @@ macro_rules! define_leaf_type { } } - impl<'a> IsValueContent<'a> for $leaf_type { + impl<'a> IsValueContent<'a> for $content_type { type Type = $type_def; type Form = BeOwned; } - impl<'a> IntoValueContent<'a> for $leaf_type { + impl HasLeafKind for $content_type { + type LeafKind = $kind; + + fn kind(&self) -> Self::LeafKind { + $kind + } + } + + impl<'a> IntoValueContent<'a> for $content_type { fn into_content(self) -> Self { self } } - impl<'a> FromValueContent<'a> for $leaf_type { + impl<'a> FromValueContent<'a> for $content_type { fn from_content(content: Self) -> Self { content } } - impl IsValueLeaf for $leaf_type {} - impl CastDyn for $leaf_type {} + impl IsValueLeaf for $content_type {} + impl CastDyn for $content_type {} impl_ancestor_chain_conversions!( $type_def => $parent($parent_content :: $parent_variant) $(=> $ancestor)* diff --git a/src/expressions/equality.rs b/src/expressions/equality.rs index 708cefb8..4fe2a2b0 100644 --- a/src/expressions/equality.rs +++ b/src/expressions/equality.rs @@ -53,8 +53,7 @@ pub(crate) trait EqualityContext { fn leaf_values_not_equal(&mut self, lhs: &T, rhs: &T) -> Self::Result; /// Values have different kinds. - fn kind_mismatch(&mut self, lhs: &L, rhs: &R) - -> Self::Result; + fn kind_mismatch(&mut self, lhs: &L, rhs: &R) -> Self::Result; /// Range values have different structures. fn range_structure_mismatch( @@ -112,7 +111,7 @@ impl EqualityContext for SimpleEquality { } #[inline] - fn kind_mismatch(&mut self, _lhs: &L, _rhs: &R) -> bool { + fn kind_mismatch(&mut self, _lhs: &L, _rhs: &R) -> bool { false } @@ -190,7 +189,7 @@ impl EqualityContext for TypedEquality { Ok(false) } - fn kind_mismatch( + fn kind_mismatch( &mut self, lhs: &L, rhs: &R, @@ -294,7 +293,7 @@ pub(crate) enum MissingSide { } /// The reason why two values were not equal. -#[derive(Debug, Clone)] +#[derive(Clone)] pub(crate) enum DebugInequalityReason { /// Values of the same type have different values. ValueMismatch { @@ -302,9 +301,9 @@ pub(crate) enum DebugInequalityReason { rhs_display: String, }, /// Values have incompatible value kinds. - ValueKindMismatch { - lhs_kind: ValueKind, - rhs_kind: ValueKind, + ValueLeafKindMismatch { + lhs_kind: ValueLeafKind, + rhs_kind: ValueLeafKind, }, /// Ranges have incompatible structures. RangeStructureMismatch { @@ -347,7 +346,7 @@ impl DebugEqualityError { path_str, path_str, lhs_display, rhs_display ) } - DebugInequalityReason::ValueKindMismatch { lhs_kind, rhs_kind } => { + DebugInequalityReason::ValueLeafKindMismatch { lhs_kind, rhs_kind } => { format!( "lhs{} is {}, but rhs{} is {}", path_str, @@ -439,14 +438,14 @@ impl EqualityContext for DebugEquality { })? } - fn kind_mismatch( + fn kind_mismatch( &mut self, lhs: &L, rhs: &R, ) -> Result<(), DebugEqualityError> { Err(DebugEqualityErrorInner { path: self.path.clone(), - reason: DebugInequalityReason::ValueKindMismatch { + reason: DebugInequalityReason::ValueLeafKindMismatch { lhs_kind: lhs.value_kind(), rhs_kind: rhs.value_kind(), }, @@ -556,7 +555,7 @@ impl EqualityContext for DebugEquality { /// - `SimpleEquality`: Returns `false` on type mismatch (like JS `===`) /// - `TypedEquality`: Errors on type mismatch with path information /// - `DebugEquality`: Returns detailed error info for assertion messages -pub(crate) trait ValuesEqual: Sized + HasValueKind { +pub(crate) trait ValuesEqual: Sized + HasLeafKind { /// Compare two values for equality using the given context. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result; diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 0a3ae5dd..3c480d5d 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -794,7 +794,10 @@ impl EvaluationFrame for UnaryOperationBuilder { let operand_kind = operand.kind(); // Try method resolution first - if let Some(interface) = operand_kind.resolve_unary_operation(&self.operation) { + if let Some(interface) = operand_kind + .method_resolver() + .resolve_unary_operation(&self.operation) + { let operand_span = operand.span_range(); let resolved_value = operand.resolve(interface.argument_ownership())?; let result = @@ -866,7 +869,10 @@ impl EvaluationFrame for BinaryOperationBuilder { .return_returned_value(Spanned(ReturnedValue::Owned(result), left_span))? } else { // Try method resolution based on left operand's kind and resolve left operand immediately - let interface = left.kind().resolve_binary_operation(&self.operation); + let interface = left + .kind() + .method_resolver() + .resolve_binary_operation(&self.operation); match interface { Some(interface) => { @@ -1256,6 +1262,7 @@ impl EvaluationFrame for MethodCallBuilder { let caller = value.expect_late_bound(); let method = caller .kind() + .method_resolver() .resolve_method(self.method.method.to_string().as_str()); let method = match method { Some(m) => m, diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index ca6e82e5..f04f3438 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -87,13 +87,17 @@ impl UnaryOperation { Spanned(input, input_span): Spanned>, ) -> ExecutionResult> { let input = input.into_owned_value(); - let method = input.kind().resolve_unary_operation(self).ok_or_else(|| { - self.type_error(format!( - "The {} operator is not supported for {} operand", - self.symbolic_description(), - input.articled_kind(), - )) - })?; + let method = input + .kind() + .method_resolver() + .resolve_unary_operation(self) + .ok_or_else(|| { + self.type_error(format!( + "The {} operator is not supported for {} operand", + self.symbolic_description(), + input.articled_kind(), + )) + })?; let input = method .argument_ownership .map_from_owned(Spanned(input, input_span))?; @@ -103,8 +107,8 @@ impl UnaryOperation { #[derive(Copy, Clone)] pub(crate) enum CastTarget { - Integer(IntegerKind), - Float(FloatKind), + Integer(IntegerLeafKind), + Float(FloatLeafKind), Boolean, String, Char, @@ -115,37 +119,40 @@ impl CastTarget { fn from_source_type(s: TypeIdent) -> Option { Some(match s.kind { TypeKind::Parent(ParentTypeKind::Integer(_)) => { - CastTarget::Integer(IntegerKind::Untyped) + CastTarget::Integer(IntegerLeafKind::Untyped(UntypedIntegerKind)) + } + TypeKind::Leaf(ValueLeafKind::Integer(kind)) => CastTarget::Integer(kind), + TypeKind::Parent(ParentTypeKind::Float(_)) => { + CastTarget::Float(FloatLeafKind::Untyped(UntypedFloatKind)) } - TypeKind::Leaf(ValueKind::Integer(kind)) => CastTarget::Integer(kind), - TypeKind::Parent(ParentTypeKind::Float(_)) => CastTarget::Float(FloatKind::Untyped), - TypeKind::Leaf(ValueKind::Float(kind)) => CastTarget::Float(kind), - TypeKind::Leaf(ValueKind::Boolean) => CastTarget::Boolean, - TypeKind::Leaf(ValueKind::String) => CastTarget::String, - TypeKind::Leaf(ValueKind::Char) => CastTarget::Char, - TypeKind::Leaf(ValueKind::Stream) => CastTarget::Stream, + TypeKind::Leaf(ValueLeafKind::Float(kind)) => CastTarget::Float(kind), + TypeKind::Leaf(ValueLeafKind::Bool(_)) => CastTarget::Boolean, + TypeKind::Leaf(ValueLeafKind::String(_)) => CastTarget::String, + TypeKind::Leaf(ValueLeafKind::Char(_)) => CastTarget::Char, + TypeKind::Leaf(ValueLeafKind::Stream(_)) => CastTarget::Stream, _ => return None, }) } + // TODO[concepts]: Improve this / align with the type name fn symbolic_description(&self) -> &'static str { match self { - CastTarget::Integer(IntegerKind::Untyped) => "as int", - CastTarget::Integer(IntegerKind::U8) => "as u8", - CastTarget::Integer(IntegerKind::U16) => "as u16", - CastTarget::Integer(IntegerKind::U32) => "as u32", - CastTarget::Integer(IntegerKind::U64) => "as u64", - CastTarget::Integer(IntegerKind::U128) => "as u128", - CastTarget::Integer(IntegerKind::Usize) => "as usize", - CastTarget::Integer(IntegerKind::I8) => "as i8", - CastTarget::Integer(IntegerKind::I16) => "as i16", - CastTarget::Integer(IntegerKind::I32) => "as i32", - CastTarget::Integer(IntegerKind::I64) => "as i64", - CastTarget::Integer(IntegerKind::I128) => "as i128", - CastTarget::Integer(IntegerKind::Isize) => "as isize", - CastTarget::Float(FloatKind::Untyped) => "as float", - CastTarget::Float(FloatKind::F32) => "as f32", - CastTarget::Float(FloatKind::F64) => "as f64", + CastTarget::Integer(IntegerLeafKind::Untyped(_)) => "as int", + CastTarget::Integer(IntegerLeafKind::U8(_)) => "as u8", + CastTarget::Integer(IntegerLeafKind::U16(_)) => "as u16", + CastTarget::Integer(IntegerLeafKind::U32(_)) => "as u32", + CastTarget::Integer(IntegerLeafKind::U64(_)) => "as u64", + CastTarget::Integer(IntegerLeafKind::U128(_)) => "as u128", + CastTarget::Integer(IntegerLeafKind::Usize(_)) => "as usize", + CastTarget::Integer(IntegerLeafKind::I8(_)) => "as i8", + CastTarget::Integer(IntegerLeafKind::I16(_)) => "as i16", + CastTarget::Integer(IntegerLeafKind::I32(_)) => "as i32", + CastTarget::Integer(IntegerLeafKind::I64(_)) => "as i64", + CastTarget::Integer(IntegerLeafKind::I128(_)) => "as i128", + CastTarget::Integer(IntegerLeafKind::Isize(_)) => "as isize", + CastTarget::Float(FloatLeafKind::Untyped(_)) => "as float", + CastTarget::Float(FloatLeafKind::F32(_)) => "as f32", + CastTarget::Float(FloatLeafKind::F64(_)) => "as f64", CastTarget::Boolean => "as bool", CastTarget::String => "as string", CastTarget::Char => "as char", @@ -325,7 +332,7 @@ impl BinaryOperation { ) -> ExecutionResult> { let left = left.into_owned_value(); let right = right.into_owned_value(); - match left.kind().resolve_binary_operation(self) { + match left.kind().method_resolver().resolve_binary_operation(self) { Some(interface) => { let left = interface .lhs_ownership diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index ed832ad8..cc7ff338 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -26,7 +26,7 @@ impl<'a> ResolutionContext<'a> { } /// Create an error for the resolution context. - pub(crate) fn err( + pub(crate) fn err( &self, articled_expected_value_kind: &str, value: V, diff --git a/src/expressions/type_resolution/value_kinds.rs b/src/expressions/type_resolution/value_kinds.rs index b04eb0f5..37dc376c 100644 --- a/src/expressions/type_resolution/value_kinds.rs +++ b/src/expressions/type_resolution/value_kinds.rs @@ -1,167 +1,49 @@ use super::*; /// A trait for specific value kinds that can provide a display name. -/// This is implemented by `ValueKind`, `IntegerKind`, `FloatKind`, etc. -pub(crate) trait IsSpecificLeafKind: Copy + Into { - fn method_resolver(&self) -> &'static dyn MethodResolver; +/// This is implemented by `ValueLeafKind`, `IntegerKind`, `FloatKind`, etc. +pub(crate) trait IsSpecificLeafKind: Copy + Into { + fn source_type_name(&self) -> &'static str; fn articled_display_name(&self) -> &'static str; + fn method_resolver(&self) -> &'static dyn MethodResolver; } -/// A trait for types that have a value kind. -pub(crate) trait HasValueKind { - type SpecificKind: IsSpecificLeafKind; - - fn kind(&self) -> Self::SpecificKind; - - fn value_kind(&self) -> ValueKind { - self.kind().into() - } - - fn articled_kind(&self) -> &'static str { - self.kind().articled_display_name() - } -} - -impl HasValueKind for &T { - type SpecificKind = T::SpecificKind; - - fn kind(&self) -> Self::SpecificKind { - (**self).kind() - } -} - -impl HasValueKind for &mut T { - type SpecificKind = T::SpecificKind; - - fn kind(&self) -> Self::SpecificKind { - (**self).kind() - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum ValueKind { - None, - Integer(IntegerKind), - Float(FloatKind), - Boolean, - String, - Char, - UnsupportedLiteral, - Array, - Object, - Stream, - Range, - Iterator, - Parser, -} - -impl ValueKind { +impl ValueLeafKind { pub(crate) fn from_source_name(name: &str) -> Option { Some(match name { - "none" => ValueKind::None, - "untyped_int" => ValueKind::Integer(IntegerKind::Untyped), - "i8" => ValueKind::Integer(IntegerKind::I8), - "i16" => ValueKind::Integer(IntegerKind::I16), - "i32" => ValueKind::Integer(IntegerKind::I32), - "i64" => ValueKind::Integer(IntegerKind::I64), - "i128" => ValueKind::Integer(IntegerKind::I128), - "isize" => ValueKind::Integer(IntegerKind::Isize), - "u8" => ValueKind::Integer(IntegerKind::U8), - "u16" => ValueKind::Integer(IntegerKind::U16), - "u32" => ValueKind::Integer(IntegerKind::U32), - "u64" => ValueKind::Integer(IntegerKind::U64), - "u128" => ValueKind::Integer(IntegerKind::U128), - "usize" => ValueKind::Integer(IntegerKind::Usize), - "untyped_float" => ValueKind::Float(FloatKind::Untyped), - "f32" => ValueKind::Float(FloatKind::F32), - "f64" => ValueKind::Float(FloatKind::F64), - "bool" => ValueKind::Boolean, - "string" => ValueKind::String, - "unsupported_literal" => ValueKind::UnsupportedLiteral, - "char" => ValueKind::Char, - "array" => ValueKind::Array, - "object" => ValueKind::Object, - "stream" => ValueKind::Stream, - "range" => ValueKind::Range, - "iterator" => ValueKind::Iterator, - "parser" => ValueKind::Parser, + "none" => ValueLeafKind::None(NoneKind), + "untyped_int" => ValueLeafKind::Integer(IntegerLeafKind::Untyped(UntypedIntegerKind)), + "i8" => ValueLeafKind::Integer(IntegerLeafKind::I8(I8Kind)), + "i16" => ValueLeafKind::Integer(IntegerLeafKind::I16(I16Kind)), + "i32" => ValueLeafKind::Integer(IntegerLeafKind::I32(I32Kind)), + "i64" => ValueLeafKind::Integer(IntegerLeafKind::I64(I64Kind)), + "i128" => ValueLeafKind::Integer(IntegerLeafKind::I128(I128Kind)), + "isize" => ValueLeafKind::Integer(IntegerLeafKind::Isize(IsizeKind)), + "u8" => ValueLeafKind::Integer(IntegerLeafKind::U8(U8Kind)), + "u16" => ValueLeafKind::Integer(IntegerLeafKind::U16(U16Kind)), + "u32" => ValueLeafKind::Integer(IntegerLeafKind::U32(U32Kind)), + "u64" => ValueLeafKind::Integer(IntegerLeafKind::U64(U64Kind)), + "u128" => ValueLeafKind::Integer(IntegerLeafKind::U128(U128Kind)), + "usize" => ValueLeafKind::Integer(IntegerLeafKind::Usize(UsizeKind)), + "untyped_float" => ValueLeafKind::Float(FloatLeafKind::Untyped(UntypedFloatKind)), + "f32" => ValueLeafKind::Float(FloatLeafKind::F32(F32Kind)), + "f64" => ValueLeafKind::Float(FloatLeafKind::F64(F64Kind)), + "bool" => ValueLeafKind::Bool(BoolKind), + "string" => ValueLeafKind::String(StringKind), + "unsupported_literal" => ValueLeafKind::UnsupportedLiteral(UnsupportedLiteralKind), + "char" => ValueLeafKind::Char(CharKind), + "array" => ValueLeafKind::Array(ArrayKind), + "object" => ValueLeafKind::Object(ObjectKind), + "stream" => ValueLeafKind::Stream(StreamKind), + "range" => ValueLeafKind::Range(RangeKind), + "iterator" => ValueLeafKind::Iterator(IteratorKind), + "parser" => ValueLeafKind::Parser(ParserKind), _ => return None, }) } - - pub(crate) fn source_name(&self) -> &'static str { - match self { - ValueKind::None => "none", - ValueKind::Integer(IntegerKind::Untyped) => "untyped_int", - ValueKind::Integer(IntegerKind::I8) => "i8", - ValueKind::Integer(IntegerKind::I16) => "i16", - ValueKind::Integer(IntegerKind::I32) => "i32", - ValueKind::Integer(IntegerKind::I64) => "i64", - ValueKind::Integer(IntegerKind::I128) => "i128", - ValueKind::Integer(IntegerKind::Isize) => "isize", - ValueKind::Integer(IntegerKind::U8) => "u8", - ValueKind::Integer(IntegerKind::U16) => "u16", - ValueKind::Integer(IntegerKind::U32) => "u32", - ValueKind::Integer(IntegerKind::U64) => "u64", - ValueKind::Integer(IntegerKind::U128) => "u128", - ValueKind::Integer(IntegerKind::Usize) => "usize", - ValueKind::Float(FloatKind::Untyped) => "untyped_float", - ValueKind::Float(FloatKind::F32) => "f32", - ValueKind::Float(FloatKind::F64) => "f64", - ValueKind::Boolean => "bool", - ValueKind::String => "string", - ValueKind::Char => "char", - ValueKind::UnsupportedLiteral => "unsupported_literal", - ValueKind::Array => "array", - ValueKind::Object => "object", - ValueKind::Stream => "stream", - ValueKind::Range => "range", - ValueKind::Iterator => "iterator", - ValueKind::Parser => "parser", - } - } -} - -impl IsSpecificLeafKind for ValueKind { - fn articled_display_name(&self) -> &'static str { - match self { - // Instead of saying "expected a none value", we can say "expected None" - ValueKind::None => "None", - ValueKind::Integer(kind) => kind.articled_display_name(), - ValueKind::Float(kind) => kind.articled_display_name(), - ValueKind::Boolean => "a bool", - ValueKind::String => "a string", - ValueKind::Char => "a char", - ValueKind::UnsupportedLiteral => "an unsupported literal", - ValueKind::Array => "an array", - ValueKind::Object => "an object", - ValueKind::Stream => "a stream", - ValueKind::Range => "a range", - ValueKind::Iterator => "an iterator", - ValueKind::Parser => "a parser", - } - } - - fn method_resolver(&self) -> &'static dyn MethodResolver { - match self { - ValueKind::None => &NoneTypeData, - ValueKind::Integer(kind) => kind.method_resolver(), - ValueKind::Float(kind) => kind.method_resolver(), - ValueKind::Boolean => &BooleanTypeData, - ValueKind::String => &StringTypeData, - ValueKind::Char => &CharTypeData, - ValueKind::UnsupportedLiteral => &UnsupportedLiteralTypeData, - ValueKind::Array => &ArrayTypeData, - ValueKind::Object => &ObjectTypeData, - ValueKind::Stream => &StreamTypeData, - ValueKind::Range => &RangeTypeData, - ValueKind::Iterator => &IteratorTypeData, - ValueKind::Parser => &ParserTypeData, - } - } } -impl ValueKind { +impl ValueLeafKind { /// This should be true for types which users expect to have value /// semantics, but false for types which are expensive to clone or /// are expected to have reference semantics. @@ -170,153 +52,37 @@ impl ValueKind { /// when doing method resolution. pub(crate) fn supports_transparent_cloning(&self) -> bool { match self { - ValueKind::None => true, - ValueKind::Integer(_) => true, - ValueKind::Float(_) => true, - ValueKind::Boolean => true, + ValueLeafKind::None(_) => true, + ValueLeafKind::Integer(_) => true, + ValueLeafKind::Float(_) => true, + ValueLeafKind::Bool(_) => true, // Strings are value-like, so it makes sense to transparently clone them - ValueKind::String => true, - ValueKind::Char => true, - ValueKind::UnsupportedLiteral => false, - ValueKind::Array => false, - ValueKind::Object => false, - ValueKind::Stream => false, - ValueKind::Range => true, - ValueKind::Iterator => false, + ValueLeafKind::String(_) => true, + ValueLeafKind::Char(_) => true, + ValueLeafKind::UnsupportedLiteral(_) => false, + ValueLeafKind::Array(_) => false, + ValueLeafKind::Object(_) => false, + ValueLeafKind::Stream(_) => false, + ValueLeafKind::Range(_) => true, + ValueLeafKind::Iterator(_) => false, // A parser is a handle, so can be cloned transparently. // It may fail to be able to be used to parse if the underlying stream is out of scope of course. - ValueKind::Parser => true, - } - } -} - -impl MethodResolver for ValueKind { - fn resolve_method(&self, method_name: &str) -> Option { - self.method_resolver().resolve_method(method_name) - } - - fn resolve_unary_operation( - &self, - operation: &UnaryOperation, - ) -> Option { - self.method_resolver().resolve_unary_operation(operation) - } - - fn resolve_binary_operation( - &self, - operation: &BinaryOperation, - ) -> Option { - self.method_resolver().resolve_binary_operation(operation) - } - - fn resolve_type_property(&self, property_name: &str) -> Option { - self.method_resolver().resolve_type_property(property_name) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum IntegerKind { - Untyped, - I8, - I16, - I32, - I64, - I128, - Isize, - U8, - U16, - U32, - U64, - U128, - Usize, -} - -impl IsSpecificLeafKind for IntegerKind { - fn articled_display_name(&self) -> &'static str { - match self { - IntegerKind::Untyped => "an untyped integer", - IntegerKind::I8 => "an i8", - IntegerKind::I16 => "an i16", - IntegerKind::I32 => "an i32", - IntegerKind::I64 => "an i64", - IntegerKind::I128 => "an i128", - IntegerKind::Isize => "an isize", - IntegerKind::U8 => "a u8", - IntegerKind::U16 => "a u16", - IntegerKind::U32 => "a u32", - IntegerKind::U64 => "a u64", - IntegerKind::U128 => "a u128", - IntegerKind::Usize => "a usize", - } - } - - fn method_resolver(&self) -> &'static dyn MethodResolver { - match self { - IntegerKind::Untyped => &UntypedIntegerTypeData, - IntegerKind::I8 => &I8TypeData, - IntegerKind::I16 => &I16TypeData, - IntegerKind::I32 => &I32TypeData, - IntegerKind::I64 => &I64TypeData, - IntegerKind::I128 => &I128TypeData, - IntegerKind::Isize => &IsizeTypeData, - IntegerKind::U8 => &U8TypeData, - IntegerKind::U16 => &U16TypeData, - IntegerKind::U32 => &U32TypeData, - IntegerKind::U64 => &U64TypeData, - IntegerKind::U128 => &U128TypeData, - IntegerKind::Usize => &UsizeTypeData, - } - } -} - -impl From for ValueKind { - fn from(kind: IntegerKind) -> Self { - ValueKind::Integer(kind) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum FloatKind { - Untyped, - F32, - F64, -} - -impl IsSpecificLeafKind for FloatKind { - fn articled_display_name(&self) -> &'static str { - match self { - FloatKind::Untyped => "an untyped float", - FloatKind::F32 => "an f32", - FloatKind::F64 => "an f64", + ValueLeafKind::Parser(_) => true, } } - - fn method_resolver(&self) -> &'static dyn MethodResolver { - match self { - FloatKind::Untyped => &UntypedFloatTypeData, - FloatKind::F32 => &F32TypeData, - FloatKind::F64 => &F64TypeData, - } - } -} - -impl From for ValueKind { - fn from(kind: FloatKind) -> Self { - ValueKind::Float(kind) - } } -// A ValueKind represents a kind of leaf value. +// A ValueLeafKind represents a kind of leaf value. // But a TypeKind represents a type in the hierarchy, which points at a type data. pub(crate) enum TypeKind { - Leaf(ValueKind), + Leaf(ValueLeafKind), Parent(ParentTypeKind), Dyn(DynTypeKind), } impl TypeKind { pub(crate) fn from_source_name(name: &str) -> Option { - if let Some(kind) = ValueKind::from_source_name(name) { + if let Some(kind) = ValueLeafKind::from_source_name(name) { return Some(TypeKind::Leaf(kind)); } if let Some(kind) = ParentTypeKind::from_source_name(name) { @@ -330,7 +96,7 @@ impl TypeKind { pub(crate) fn source_name(&self) -> &'static str { match self { - TypeKind::Leaf(leaf_kind) => leaf_kind.source_name(), + TypeKind::Leaf(leaf_kind) => leaf_kind.source_type_name(), TypeKind::Parent(parent_kind) => parent_kind.source_name(), TypeKind::Dyn(dyn_kind) => dyn_kind.source_name(), } diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 2a9beac5..9b7fe5e2 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -143,14 +143,6 @@ impl ArrayValue { } } -impl HasValueKind for ArrayValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Array - } -} - impl ValuesEqual for ArrayValue { /// Recursively compares two arrays element-by-element. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { diff --git a/src/expressions/values/boolean.rs b/src/expressions/values/boolean.rs index 1601f878..28cb9cf0 100644 --- a/src/expressions/values/boolean.rs +++ b/src/expressions/values/boolean.rs @@ -7,7 +7,7 @@ define_leaf_type! { content: bool, kind: pub(crate) BoolKind, type_name: "bool", - articled_display_name: "a boolean", + articled_display_name: "a bool", temp_type_data: BooleanTypeData, } @@ -38,11 +38,11 @@ impl BooleanValue { } } -impl HasValueKind for BooleanValue { - type SpecificKind = ValueKind; +impl HasLeafKind for BooleanValue { + type LeafKind = BoolKind; - fn kind(&self) -> ValueKind { - ValueKind::Boolean + fn kind(&self) -> Self::LeafKind { + BoolKind } } @@ -201,19 +201,19 @@ define_interface! { Some(match operation { UnaryOperation::Not { .. } => unary_definitions::not(), UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), + CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), CastTarget::Boolean => unary_definitions::cast_to_boolean(), CastTarget::String => unary_definitions::cast_to_string(), _ => return None, @@ -230,7 +230,7 @@ impl_resolvable_argument_for! { (value, context) -> BooleanValue { match value { Value::Boolean(value) => Ok(value), - other => context.err("a boolean", other), + other => context.err("a bool", other), } } } diff --git a/src/expressions/values/character.rs b/src/expressions/values/character.rs index 8f63373d..9f05f680 100644 --- a/src/expressions/values/character.rs +++ b/src/expressions/values/character.rs @@ -36,11 +36,11 @@ impl CharValue { } } -impl HasValueKind for CharValue { - type SpecificKind = ValueKind; +impl HasLeafKind for CharValue { + type LeafKind = CharKind; - fn kind(&self) -> ValueKind { - ValueKind::Char + fn kind(&self) -> Self::LeafKind { + CharKind } } @@ -170,19 +170,19 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), + CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), CastTarget::Char => unary_definitions::cast_to_char(), CastTarget::String => unary_definitions::cast_to_string(), _ => return None, diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index d61b47a2..826f4e81 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -120,18 +120,6 @@ impl FloatValue { } } -impl HasValueKind for FloatValue { - type SpecificKind = FloatKind; - - fn kind(&self) -> FloatKind { - match self { - Self::Untyped(_) => FloatKind::Untyped, - Self::F32(_) => FloatKind::F32, - Self::F64(_) => FloatKind::F64, - } - } -} - impl Debug for FloatValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -142,6 +130,19 @@ impl Debug for FloatValue { } } +// TODO[concepts]: Remove when this is auto-generated after Value changes +impl HasLeafKind for FloatValue { + type LeafKind = FloatLeafKind; + + fn kind(&self) -> FloatLeafKind { + match self { + FloatValue::Untyped(_) => FloatLeafKind::Untyped(UntypedFloatKind), + FloatValue::F32(_) => FloatLeafKind::F32(F32Kind), + FloatValue::F64(_) => FloatLeafKind::F64(F64Kind), + } + } +} + impl FloatValue { /// Aligns types for comparison - converts untyped to match the other's type. /// Unlike integers, float conversion never fails (may lose precision). diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 372fbaee..04bbf4fd 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -90,22 +90,22 @@ macro_rules! impl_float_operations { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatKind::Untyped) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatKind::F32) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatKind::F64) => unary_definitions::cast_to_f64(), + CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + CastTarget::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + CastTarget::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + CastTarget::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), CastTarget::String => unary_definitions::cast_to_string(), _ => return None, } @@ -138,14 +138,6 @@ macro_rules! impl_float_operations { } } - impl HasValueKind for $float_type { - type SpecificKind = FloatKind; - - fn kind(&self) -> FloatKind { - FloatKind::$float_enum_variant - } - } - impl IntoValue for $float_type { fn into_value(self) -> Value { Value::Float(FloatValue::$float_enum_variant(self)) diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index fe3ee8bf..b76bffe1 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -26,11 +26,11 @@ impl UntypedFloat { /// Converts an untyped float to a specific float kind. /// Unlike integers, float conversion never fails (may lose precision). - pub(crate) fn into_kind(self, kind: FloatKind) -> FloatValue { + pub(crate) fn into_kind(self, kind: FloatLeafKind) -> FloatValue { match kind { - FloatKind::Untyped => FloatValue::Untyped(self), - FloatKind::F32 => FloatValue::F32(self.0 as f32), - FloatKind::F64 => FloatValue::F64(self.0), + FloatLeafKind::Untyped(_) => FloatValue::Untyped(self), + FloatLeafKind::F32(_) => FloatValue::F32(self.0 as f32), + FloatLeafKind::F64(_) => FloatValue::F64(self.0), } } @@ -70,14 +70,6 @@ impl UntypedFloat { } } -impl HasValueKind for UntypedFloat { - type SpecificKind = FloatKind; - - fn kind(&self) -> FloatKind { - FloatKind::Untyped - } -} - impl IntoValue for UntypedFloat { fn into_value(self) -> Value { Value::Float(FloatValue::Untyped(self)) @@ -170,22 +162,22 @@ define_interface! { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatKind::Untyped) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatKind::F32) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatKind::F64) => unary_definitions::cast_to_f64(), + CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + CastTarget::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + CastTarget::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + CastTarget::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), CastTarget::String => unary_definitions::cast_to_string(), _ => return None, }, diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index 33de206f..cf0c3b2d 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -134,24 +134,25 @@ impl IntegerValue { } } -impl HasValueKind for IntegerValue { - type SpecificKind = IntegerKind; +// TODO[concepts]: Remove when this is auto-generated after Value changes +impl HasLeafKind for IntegerValue { + type LeafKind = IntegerLeafKind; - fn kind(&self) -> IntegerKind { + fn kind(&self) -> IntegerLeafKind { match self { - Self::Untyped(_) => IntegerKind::Untyped, - Self::U8(_) => IntegerKind::U8, - Self::U16(_) => IntegerKind::U16, - Self::U32(_) => IntegerKind::U32, - Self::U64(_) => IntegerKind::U64, - Self::U128(_) => IntegerKind::U128, - Self::Usize(_) => IntegerKind::Usize, - Self::I8(_) => IntegerKind::I8, - Self::I16(_) => IntegerKind::I16, - Self::I32(_) => IntegerKind::I32, - Self::I64(_) => IntegerKind::I64, - Self::I128(_) => IntegerKind::I128, - Self::Isize(_) => IntegerKind::Isize, + IntegerValue::Untyped(_) => IntegerLeafKind::Untyped(UntypedIntegerKind), + IntegerValue::U8(_) => IntegerLeafKind::U8(U8Kind), + IntegerValue::U16(_) => IntegerLeafKind::U16(U16Kind), + IntegerValue::U32(_) => IntegerLeafKind::U32(U32Kind), + IntegerValue::U64(_) => IntegerLeafKind::U64(U64Kind), + IntegerValue::U128(_) => IntegerLeafKind::U128(U128Kind), + IntegerValue::Usize(_) => IntegerLeafKind::Usize(UsizeKind), + IntegerValue::I8(_) => IntegerLeafKind::I8(I8Kind), + IntegerValue::I16(_) => IntegerLeafKind::I16(I16Kind), + IntegerValue::I32(_) => IntegerLeafKind::I32(I32Kind), + IntegerValue::I64(_) => IntegerLeafKind::I64(I64Kind), + IntegerValue::I128(_) => IntegerLeafKind::I128(I128Kind), + IntegerValue::Isize(_) => IntegerLeafKind::Isize(IsizeKind), } } } diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index beca4729..a4a2520b 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -116,22 +116,22 @@ macro_rules! impl_int_operations { unary_definitions::cast_to_char() } )? - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatKind::Untyped) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatKind::F32) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatKind::F64) => unary_definitions::cast_to_f64(), + CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + CastTarget::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + CastTarget::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + CastTarget::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), CastTarget::String => unary_definitions::cast_to_string(), _ => return None, } @@ -159,14 +159,6 @@ macro_rules! impl_int_operations { } } - impl HasValueKind for $integer_type { - type SpecificKind = IntegerKind; - - fn kind(&self) -> IntegerKind { - IntegerKind::$integer_enum_variant - } - } - impl IntoValue for $integer_type { fn into_value(self) -> Value { Value::Integer(IntegerValue::$integer_enum_variant(self)) diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index 71567bb3..90fa21d1 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -39,22 +39,22 @@ impl UntypedInteger { } /// Tries to convert to a specific integer kind, returning None if the value doesn't fit. - pub(crate) fn try_into_kind(self, kind: IntegerKind) -> Option { + pub(crate) fn try_into_kind(self, kind: IntegerLeafKind) -> Option { let value = self.0; Some(match kind { - IntegerKind::Untyped => IntegerValue::Untyped(UntypedInteger(value)), - IntegerKind::I8 => IntegerValue::I8(value.try_into().ok()?), - IntegerKind::I16 => IntegerValue::I16(value.try_into().ok()?), - IntegerKind::I32 => IntegerValue::I32(value.try_into().ok()?), - IntegerKind::I64 => IntegerValue::I64(value.try_into().ok()?), - IntegerKind::I128 => IntegerValue::I128(value), - IntegerKind::Isize => IntegerValue::Isize(value.try_into().ok()?), - IntegerKind::U8 => IntegerValue::U8(value.try_into().ok()?), - IntegerKind::U16 => IntegerValue::U16(value.try_into().ok()?), - IntegerKind::U32 => IntegerValue::U32(value.try_into().ok()?), - IntegerKind::U64 => IntegerValue::U64(value.try_into().ok()?), - IntegerKind::U128 => IntegerValue::U128(value.try_into().ok()?), - IntegerKind::Usize => IntegerValue::Usize(value.try_into().ok()?), + IntegerLeafKind::Untyped(_) => IntegerValue::Untyped(UntypedInteger(value)), + IntegerLeafKind::I8(_) => IntegerValue::I8(value.try_into().ok()?), + IntegerLeafKind::I16(_) => IntegerValue::I16(value.try_into().ok()?), + IntegerLeafKind::I32(_) => IntegerValue::I32(value.try_into().ok()?), + IntegerLeafKind::I64(_) => IntegerValue::I64(value.try_into().ok()?), + IntegerLeafKind::I128(_) => IntegerValue::I128(value), + IntegerLeafKind::Isize(_) => IntegerValue::Isize(value.try_into().ok()?), + IntegerLeafKind::U8(_) => IntegerValue::U8(value.try_into().ok()?), + IntegerLeafKind::U16(_) => IntegerValue::U16(value.try_into().ok()?), + IntegerLeafKind::U32(_) => IntegerValue::U32(value.try_into().ok()?), + IntegerLeafKind::U64(_) => IntegerValue::U64(value.try_into().ok()?), + IntegerLeafKind::U128(_) => IntegerValue::U128(value.try_into().ok()?), + IntegerLeafKind::Usize(_) => IntegerValue::Usize(value.try_into().ok()?), }) } @@ -109,7 +109,7 @@ impl UntypedInteger { } impl Spanned { - pub(crate) fn into_kind(self, kind: IntegerKind) -> ExecutionResult { + pub(crate) fn into_kind(self, kind: IntegerLeafKind) -> ExecutionResult { let Spanned(value, span_range) = self; value.try_into_kind(kind).ok_or_else(|| { span_range.value_error(format!( @@ -121,14 +121,6 @@ impl Spanned { } } -impl HasValueKind for UntypedInteger { - type SpecificKind = IntegerKind; - - fn kind(&self) -> IntegerKind { - IntegerKind::Untyped - } -} - impl IntoValue for UntypedInteger { fn into_value(self) -> Value { Value::Integer(IntegerValue::Untyped(self)) @@ -226,22 +218,22 @@ define_interface! { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatKind::Untyped) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatKind::F32) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatKind::F64) => unary_definitions::cast_to_f64(), + CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + CastTarget::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + CastTarget::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + CastTarget::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), CastTarget::String => unary_definitions::cast_to_string(), _ => return None, }, diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index b6641382..b1a55c71 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -120,12 +120,14 @@ impl IsArgument for IterableRef<'static> { fn from_argument(argument: Spanned) -> ExecutionResult { Ok(match argument.kind() { - ValueKind::Iterator => IterableRef::Iterator(IsArgument::from_argument(argument)?), - ValueKind::Array => IterableRef::Array(IsArgument::from_argument(argument)?), - ValueKind::Stream => IterableRef::Stream(IsArgument::from_argument(argument)?), - ValueKind::Range => IterableRef::Range(IsArgument::from_argument(argument)?), - ValueKind::Object => IterableRef::Object(IsArgument::from_argument(argument)?), - ValueKind::String => IterableRef::String(IsArgument::from_argument(argument)?), + ValueLeafKind::Iterator(_) => { + IterableRef::Iterator(IsArgument::from_argument(argument)?) + } + ValueLeafKind::Array(_) => IterableRef::Array(IsArgument::from_argument(argument)?), + ValueLeafKind::Stream(_) => IterableRef::Stream(IsArgument::from_argument(argument)?), + ValueLeafKind::Range(_) => IterableRef::Range(IsArgument::from_argument(argument)?), + ValueLeafKind::Object(_) => IterableRef::Object(IsArgument::from_argument(argument)?), + ValueLeafKind::String(_) => IterableRef::String(IsArgument::from_argument(argument)?), _ => { return argument.type_err( "Expected iterable (iterator, array, object, stream, range or string)", diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index e6b4f765..df267d18 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -204,14 +204,6 @@ impl_resolvable_argument_for! { } } -impl HasValueKind for IteratorValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Iterator - } -} - fn definite_size_hint(size_hint: (usize, Option)) -> Option { let (min, max) = size_hint; if let Some(max) = max { diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index 279a2df7..6c24183b 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -250,14 +250,6 @@ impl Spanned<&ObjectValue> { } } -impl HasValueKind for ObjectValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Object - } -} - impl IntoValue for BTreeMap { fn into_value(self) -> Value { Value::Object(ObjectValue { entries: self }) diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 8434d1c9..1ed29a2e 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -14,11 +14,11 @@ pub(crate) struct ParserValue { handle: ParserHandle, } -impl HasValueKind for ParserValue { - type SpecificKind = ValueKind; +impl HasLeafKind for ParserValue { + type LeafKind = ParserKind; - fn kind(&self) -> ValueKind { - ValueKind::Parser + fn kind(&self) -> Self::LeafKind { + ParserKind } } diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index c343dfe6..aea075a3 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -155,14 +155,6 @@ impl RangeStructure { } } -impl HasValueKind for RangeValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Range - } -} - impl ValuesEqual for RangeValue { /// Ranges are equal if they have the same kind and the same bounds. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index f3f9c0ae..78f7f4a1 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -2,7 +2,7 @@ use super::*; define_leaf_type! { pub(crate) StreamType => ValueType(ValueContent::Stream), - content: StreamValue, + content: OutputStream, kind: pub(crate) StreamKind, type_name: "stream", articled_display_name: "a stream", @@ -65,11 +65,11 @@ impl StreamValue { } } -impl HasValueKind for StreamValue { - type SpecificKind = ValueKind; +impl HasLeafKind for StreamValue { + type LeafKind = StreamKind; - fn kind(&self) -> ValueKind { - ValueKind::Stream + fn kind(&self) -> Self::LeafKind { + StreamKind } } diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index 7697051e..2a49270a 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -36,11 +36,11 @@ impl StringValue { } } -impl HasValueKind for StringValue { - type SpecificKind = ValueKind; +impl HasLeafKind for StringValue { + type LeafKind = StringKind; - fn kind(&self) -> ValueKind { - ValueKind::String + fn kind(&self) -> Self::LeafKind { + StringKind } } diff --git a/src/expressions/values/unsupported_literal.rs b/src/expressions/values/unsupported_literal.rs index c3ed0958..11e3445d 100644 --- a/src/expressions/values/unsupported_literal.rs +++ b/src/expressions/values/unsupported_literal.rs @@ -30,11 +30,11 @@ impl Debug for UnsupportedLiteral { } } -impl HasValueKind for UnsupportedLiteral { - type SpecificKind = ValueKind; +impl HasLeafKind for UnsupportedLiteral { + type LeafKind = UnsupportedLiteralKind; - fn kind(&self) -> ValueKind { - ValueKind::UnsupportedLiteral + fn kind(&self) -> Self::LeafKind { + UnsupportedLiteralKind } } diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index 2f00d227..ca7aa6ed 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -1,11 +1,5 @@ use super::*; -impl From for ValueKind { - fn from(_kind: ValueLeafKind) -> Self { - unimplemented!() - } -} - // TODO[concepts]: Uncomment when ready // pub(crate) type QqqValue = Actual<'static, ValueType, BeOwned>; // pub(crate) type QqqValueReferencable = Actual<'static, ValueType, BeReferenceable>; @@ -624,24 +618,27 @@ pub(crate) enum Grouping { Flattened, } -impl HasValueKind for Value { - type SpecificKind = ValueKind; +// TODO[concepts]: Remove when this is auto-generated after Value changes +impl HasLeafKind for Value { + type LeafKind = ValueLeafKind; - fn kind(&self) -> ValueKind { + fn kind(&self) -> ValueLeafKind { match self { - Value::None => ValueKind::None, - Value::Integer(integer) => ValueKind::Integer(integer.kind()), - Value::Float(float) => ValueKind::Float(float.kind()), - Value::Boolean(_) => ValueKind::Boolean, - Value::String(_) => ValueKind::String, - Value::Char(_) => ValueKind::Char, - Value::Array(_) => ValueKind::Array, - Value::Object(_) => ValueKind::Object, - Value::Stream(_) => ValueKind::Stream, - Value::Range(_) => ValueKind::Range, - Value::Iterator(_) => ValueKind::Iterator, - Value::Parser(_) => ValueKind::Parser, - Value::UnsupportedLiteral(_) => ValueKind::UnsupportedLiteral, + Value::None => ValueLeafKind::None(NoneKind), + Value::Integer(integer) => ValueLeafKind::Integer(integer.kind()), + Value::Float(float) => ValueLeafKind::Float(float.kind()), + Value::Boolean(_) => ValueLeafKind::Bool(BoolKind), + Value::String(_) => ValueLeafKind::String(StringKind), + Value::Char(_) => ValueLeafKind::Char(CharKind), + Value::Array(_) => ValueLeafKind::Array(ArrayKind), + Value::Object(_) => ValueLeafKind::Object(ObjectKind), + Value::Stream(_) => ValueLeafKind::Stream(StreamKind), + Value::Range(_) => ValueLeafKind::Range(RangeKind), + Value::Iterator(_) => ValueLeafKind::Iterator(IteratorKind), + Value::Parser(_) => ValueLeafKind::Parser(ParserKind), + Value::UnsupportedLiteral(_) => { + ValueLeafKind::UnsupportedLiteral(UnsupportedLiteralKind) + } } } } diff --git a/tests/compilation_failures/operations/compare_bool_and_int.stderr b/tests/compilation_failures/operations/compare_bool_and_int.stderr index 9f7cc033..cd8c5ce4 100644 --- a/tests/compilation_failures/operations/compare_bool_and_int.stderr +++ b/tests/compilation_failures/operations/compare_bool_and_int.stderr @@ -1,4 +1,4 @@ -error: This argument is expected to be a boolean, but it is an untyped integer +error: This argument is expected to be a bool, but it is an untyped integer --> tests/compilation_failures/operations/compare_bool_and_int.rs:4:26 | 4 | let _ = run!(true == 1); diff --git a/tests/compilation_failures/operations/logical_and_on_int.stderr b/tests/compilation_failures/operations/logical_and_on_int.stderr index dcdfd0fa..0e3f47bb 100644 --- a/tests/compilation_failures/operations/logical_and_on_int.stderr +++ b/tests/compilation_failures/operations/logical_and_on_int.stderr @@ -1,4 +1,4 @@ -error: The left operand to && is expected to be a boolean, but it is an untyped integer +error: The left operand to && is expected to be a bool, but it is an untyped integer --> tests/compilation_failures/operations/logical_and_on_int.rs:4:18 | 4 | let _ = run!(1 && 2); diff --git a/tests/compilation_failures/operations/logical_or_on_int.stderr b/tests/compilation_failures/operations/logical_or_on_int.stderr index 26c068d7..ce7a0f7e 100644 --- a/tests/compilation_failures/operations/logical_or_on_int.stderr +++ b/tests/compilation_failures/operations/logical_or_on_int.stderr @@ -1,4 +1,4 @@ -error: The left operand to || is expected to be a boolean, but it is an untyped integer +error: The left operand to || is expected to be a bool, but it is an untyped integer --> tests/compilation_failures/operations/logical_or_on_int.rs:4:18 | 4 | let _ = run!(1 || 2); From 106cadc92e4d29c3c8562594ae678756ab058e94 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 3 Jan 2026 23:53:11 +0100 Subject: [PATCH 10/58] feature: Create Ref and Mut --- plans/TODO.md | 10 ++-- src/expressions/concepts/actual.rs | 22 ++++++++ src/expressions/concepts/form.rs | 14 ++++- src/expressions/concepts/forms/any_mut.rs | 19 ++++++- src/expressions/concepts/forms/any_ref.rs | 13 ++++- src/expressions/concepts/forms/argument.rs | 15 ++++- src/expressions/concepts/forms/assignee.rs | 29 ++++++++-- .../concepts/forms/copy_on_write.rs | 15 ++++- src/expressions/concepts/forms/late_bound.rs | 4 +- src/expressions/concepts/forms/mod.rs | 4 ++ src/expressions/concepts/forms/mutable.rs | 27 ++++++++- src/expressions/concepts/forms/owned.rs | 53 ++++++++++++++---- .../concepts/forms/referenceable.rs | 8 +-- src/expressions/concepts/forms/shared.rs | 21 ++++++- src/expressions/concepts/forms/simple_mut.rs | 56 +++++++++++++++++++ src/expressions/concepts/forms/simple_ref.rs | 48 ++++++++++++++++ src/expressions/concepts/type_traits.rs | 54 ++++++++++++++++++ src/internal_prelude.rs | 1 + 18 files changed, 366 insertions(+), 47 deletions(-) create mode 100644 src/expressions/concepts/forms/simple_mut.rs create mode 100644 src/expressions/concepts/forms/simple_ref.rs diff --git a/plans/TODO.md b/plans/TODO.md index 6aae7a2e..b0e85cf4 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -214,9 +214,10 @@ First, read the @./2025-11-vision.md - [ ] Find a way to generate `from_source_name` - ideally efficiently - [ ] Add ability to implement IsIterable - [ ] `CastTarget` simply wraps `TypeKind` - - [ ] Create a `Ref` and a `Mut` form - - [ ] They won't implement `IsArgumentForm` - - [ ] Create suitably generic `as_ref()` and `as_mut()` methods on `Actual` + - [x] Create a `Ref` and a `Mut` form + - [x] They won't implement `IsArgumentForm` + - [x] Create mappers and suitably generic `as_ref()` and `as_mut()` methods on `Actual` + - [ ] Migrate method resolution to the trait macro properly - [ ] Stage 1 of the form migration: - [ ] Replace `Owned` as type reference to `Actual<..>` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` @@ -230,7 +231,8 @@ First, read the @./2025-11-vision.md - [ ] Strip wrapper types like `StreamValue` - can just use `OutputStream` as content - [ ] Remove old definitions - [ ] Generate test over all value kinds which checks for: - - [ ] source type has no spaces and is lower case, and is invertible + - [ ] Source type has no spaces and is lower case, and is invertible + - [ ] Ancestor types agree with type kinds - [ ] Clear up all `TODO[concepts]` - [ ] Have variables store a `Referenceable` diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs index af96c5c3..6d70970d 100644 --- a/src/expressions/concepts/actual.rs +++ b/src/expressions/concepts/actual.rs @@ -35,6 +35,28 @@ impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> { { T::map_with::<'a, F, M>(self.0).map(|c| Actual(c)) } + + #[inline] + pub(crate) fn map_ref_with<'r, M: RefLeafMapper>( + &'r self, + ) -> Result, M::ShortCircuit<'a>> + where + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, + { + T::map_ref_with::(&self.0).map(|c| Actual(c)) + } + + #[inline] + pub(crate) fn map_mut_with<'r, M: MutLeafMapper>( + &'r mut self, + ) -> Result, M::ShortCircuit<'a>> + where + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, + { + T::map_mut_with::(&mut self.0).map(|c| Actual(c)) + } } impl<'a, T: IsType, F: IsFormOf> Spanned> { diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 4f4d6d7b..90c08c64 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -7,9 +7,7 @@ use super::*; /// - [BeShared] representing [Shared] references /// - [BeMutable] representing [Mutable] references /// - [BeCopyOnWrite] representing [CopyOnWrite] values -pub(crate) trait IsForm: Sized { - const ARGUMENT_OWNERSHIP: ArgumentOwnership; -} +pub(crate) trait IsForm: Sized {} pub(crate) trait IsFormOf: IsForm { type Content<'a>; @@ -34,6 +32,14 @@ impl IsFormOfForKind = T::Content<'a, F>; } +pub(crate) trait LeafAsRefForm: IsHierarchicalForm { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T; +} + +pub(crate) trait LeafAsMutForm: IsHierarchicalForm { + fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T; +} + pub(crate) trait IsDynCompatibleForm: IsForm { /// The container for a dyn Trait based type. /// The DynLeaf can be similar to the standard leaf, but must be @@ -52,6 +58,8 @@ pub(crate) trait IsDynMappableForm: IsHierarchicalForm + IsDynCompatibleForm { } pub(crate) trait MapFromArgument: IsFormOf { + const ARGUMENT_OWNERSHIP: ArgumentOwnership; + fn from_argument_value( value: ArgumentValue, ) -> ExecutionResult>; diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index a873e29f..fea565e9 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -1,9 +1,7 @@ use super::*; pub(crate) struct BeAnyMut; -impl IsForm for BeAnyMut { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; -} +impl IsForm for BeAnyMut {} impl IsHierarchicalForm for BeAnyMut { type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyMut<'a, T>; } @@ -20,10 +18,25 @@ impl IsDynMappableForm for BeAnyMut { } } +impl LeafAsRefForm for BeAnyMut { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + leaf + } +} + +impl LeafAsMutForm for BeAnyMut { + fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { + leaf + } +} + impl MapFromArgument for BeAnyMut { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; + fn from_argument_value( _value: ArgumentValue, ) -> ExecutionResult> { todo!() + // value.expect_mutable().as_any_mut() } } diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 5457c150..c4b299c4 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -3,9 +3,7 @@ use super::*; type QqqAnyRef<'a, T> = Actual<'a, T, BeAnyRef>; pub(crate) struct BeAnyRef; -impl IsForm for BeAnyRef { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; -} +impl IsForm for BeAnyRef {} impl IsHierarchicalForm for BeAnyRef { type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyRef<'a, T>; @@ -23,10 +21,19 @@ impl IsDynMappableForm for BeAnyRef { } } +impl LeafAsRefForm for BeAnyRef { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + leaf + } +} + impl MapFromArgument for BeAnyRef { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; + fn from_argument_value( _value: ArgumentValue, ) -> ExecutionResult> { todo!() + // value.expect_shared().as_any_ref() } } diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index c2d072b1..32759f86 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -3,14 +3,23 @@ use super::*; pub(crate) type QqqArgumentValue = Actual<'static, T, BeArgument>; pub(crate) struct BeArgument; -impl IsForm for BeArgument { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; -} +impl IsForm for BeArgument {} impl IsHierarchicalForm for BeArgument { type Leaf<'a, T: IsValueLeaf> = ArgumentContent; } +impl MapFromArgument for BeArgument { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + todo!() + // Ok(value) + } +} + pub(crate) enum ArgumentContent { Owned(T), CopyOnWrite(CopyOnWriteContent), diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index ee2eb80b..66a812b9 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -3,10 +3,7 @@ use super::*; type QqqAssignee = Actual<'static, T, BeAssignee>; pub(crate) struct BeAssignee; -impl IsForm for BeAssignee { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = - ArgumentOwnership::Assignee { auto_create: false }; -} +impl IsForm for BeAssignee {} impl IsHierarchicalForm for BeAssignee { type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; @@ -23,3 +20,27 @@ impl IsDynMappableForm for BeAssignee { leaf.map_optional(T::map_mut) } } + +impl LeafAsRefForm for BeAssignee { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + leaf + } +} + +impl LeafAsMutForm for BeAssignee { + fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { + leaf + } +} + +impl MapFromArgument for BeAssignee { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = + ArgumentOwnership::Assignee { auto_create: false }; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + todo!() + // value.expect_assignee() + } +} diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 137f3901..b895dd0e 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -3,9 +3,7 @@ use super::*; pub(crate) type QqqCopyOnWrite = Actual<'static, T, BeCopyOnWrite>; pub(crate) struct BeCopyOnWrite; -impl IsForm for BeCopyOnWrite { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; -} +impl IsForm for BeCopyOnWrite {} impl IsHierarchicalForm for BeCopyOnWrite { type Leaf<'a, T: IsValueLeaf> = CopyOnWriteContent; @@ -24,6 +22,17 @@ impl IsDynMappableForm for BeCopyOnWrite { } } +impl MapFromArgument for BeCopyOnWrite { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; + + fn from_argument_value( + _value: ArgumentValue, + ) -> ExecutionResult> { + todo!() + // value.expect_copy_on_write() + } +} + /// Typically O == T or more specifically, ::Owned pub(crate) enum CopyOnWriteContent { /// An owned value that can be used directly diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index 98f88701..c3ccb86e 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -13,9 +13,7 @@ use super::*; pub(crate) type QqqLateBound = Actual<'static, T, BeLateBound>; pub(crate) struct BeLateBound; -impl IsForm for BeLateBound { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; -} +impl IsForm for BeLateBound {} impl IsHierarchicalForm for BeLateBound { type Leaf<'a, T: IsValueLeaf> = LateBoundContent; diff --git a/src/expressions/concepts/forms/mod.rs b/src/expressions/concepts/forms/mod.rs index 2889a214..3f42e697 100644 --- a/src/expressions/concepts/forms/mod.rs +++ b/src/expressions/concepts/forms/mod.rs @@ -11,6 +11,8 @@ mod mutable; mod owned; mod referenceable; mod shared; +mod simple_mut; +mod simple_ref; pub(crate) use any_mut::*; pub(crate) use any_ref::*; @@ -22,3 +24,5 @@ pub(crate) use mutable::*; pub(crate) use owned::*; pub(crate) use referenceable::*; pub(crate) use shared::*; +pub(crate) use simple_mut::*; +pub(crate) use simple_ref::*; diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index cd07ba27..f38d6570 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -3,9 +3,7 @@ use super::*; pub(crate) type QqqMutable = Actual<'static, T, BeMutable>; pub(crate) struct BeMutable; -impl IsForm for BeMutable { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; -} +impl IsForm for BeMutable {} impl IsHierarchicalForm for BeMutable { type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; @@ -22,3 +20,26 @@ impl IsDynMappableForm for BeMutable { leaf.map_optional(T::map_mut) } } + +impl LeafAsRefForm for BeMutable { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + leaf + } +} + +impl LeafAsMutForm for BeMutable { + fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { + leaf + } +} + +impl MapFromArgument for BeMutable { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; + + fn from_argument_value( + _value: ArgumentValue, + ) -> ExecutionResult> { + // value.expect_mutable() + todo!() + } +} diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 78911495..d3fc4762 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -3,9 +3,7 @@ use super::*; pub(crate) type QqqOwned = Actual<'static, T, BeOwned>; pub(crate) struct BeOwned; -impl IsForm for BeOwned { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; -} +impl IsForm for BeOwned {} impl IsHierarchicalForm for BeOwned { type Leaf<'a, T: IsValueLeaf> = T; @@ -23,7 +21,21 @@ impl IsDynMappableForm for BeOwned { } } +impl LeafAsRefForm for BeOwned { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + leaf + } +} + +impl LeafAsMutForm for BeOwned { + fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { + leaf + } +} + impl MapFromArgument for BeOwned { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument_value( _value: ArgumentValue, ) -> ExecutionResult> { @@ -32,12 +44,31 @@ impl MapFromArgument for BeOwned { } } -#[test] -fn can_resolve_owned() { - let owned_value: QqqOwned = QqqOwned::of(42u64); - let resolved = owned_value - .spanned(Span::call_site().span_range()) - .resolve_as::("My value") - .unwrap(); - assert_eq!(resolved, 42u64); +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn can_resolve_owned() { + let owned_value: QqqOwned = QqqOwned::of(42u64); + let resolved = owned_value + .spanned(Span::call_site().span_range()) + .resolve_as::("My value") + .unwrap(); + assert_eq!(resolved, 42u64); + } + + #[test] + fn can_as_ref_owned() { + let owned_value: QqqOwned = QqqOwned::of(42u64); + let as_ref: QqqRef = owned_value.as_ref(); + assert_eq!(**as_ref, 42u64); + } + + #[test] + fn can_as_mut_owned() { + let mut owned_value: QqqOwned = QqqOwned::of(42u64); + **owned_value.as_mut() = 41u64; + assert_eq!(owned_value.0, 41u64); + } } diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index 8a517357..cd5e7215 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -8,15 +8,15 @@ type QqqReferenceable = Actual<'static, T, BeReferenceable>; /// Note that Referenceable form does not support dyn casting, because Rc> cannot be /// directly cast to Rc>. pub(crate) struct BeReferenceable; -impl IsForm for BeReferenceable { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; -} +impl IsForm for BeReferenceable {} impl IsHierarchicalForm for BeReferenceable { type Leaf<'a, T: IsValueLeaf> = Rc>; } impl MapFromArgument for BeReferenceable { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument_value( _value: ArgumentValue, ) -> ExecutionResult> { @@ -38,7 +38,7 @@ pub(crate) struct OwnedToReferencableMapper; impl LeafMapper for OwnedToReferencableMapper { type OutputForm = BeReferenceable; - type ShortCircuit<'a> = std::convert::Infallible; + type ShortCircuit<'a> = Infallible; fn map_leaf<'a, L: IsValueLeaf>(leaf: L) -> Result>, Self::ShortCircuit<'a>> { Ok(Rc::new(RefCell::new(leaf))) diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index d5074466..ce494681 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -3,9 +3,7 @@ use super::*; pub(crate) type QqqShared = Actual<'static, T, BeShared>; pub(crate) struct BeShared; -impl IsForm for BeShared { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; -} +impl IsForm for BeShared {} impl IsHierarchicalForm for BeShared { type Leaf<'a, T: IsValueLeaf> = SharedSubRcRefCell; @@ -22,3 +20,20 @@ impl IsDynMappableForm for BeShared { leaf.map_optional(T::map_ref) } } + +impl LeafAsRefForm for BeShared { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + leaf + } +} + +impl MapFromArgument for BeShared { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; + + fn from_argument_value( + _value: ArgumentValue, + ) -> ExecutionResult> { + // value.expect_shared() + todo!() + } +} diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs new file mode 100644 index 00000000..64032093 --- /dev/null +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -0,0 +1,56 @@ +use super::*; + +pub(crate) type QqqMut<'a, T> = Actual<'a, T, BeMut>; + +/// It can't be an argument because arguments must be owned in some way; +/// so that the drop glue can work properly (because they may come from +/// e.g. a reference counted Shared handle) +pub(crate) struct BeMut; +impl IsForm for BeMut {} + +impl IsHierarchicalForm for BeMut { + type Leaf<'a, T: IsValueLeaf> = &'a mut T; +} + +impl IsDynCompatibleForm for BeMut { + type DynLeaf<'a, T: 'static + ?Sized> = &'a mut T; +} + +impl IsDynMappableForm for BeMut { + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> { + T::map_mut(leaf) + } +} + +impl LeafAsRefForm for BeMut { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + leaf + } +} + +pub(crate) struct ToMutMapper; + +impl MutLeafMapper for ToMutMapper { + type OutputForm = BeMut; + type ShortCircuit<'a> = Infallible; + + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + leaf: &'r mut F::Leaf<'a, L>, + ) -> Result<&'r mut L, Infallible> { + Ok(F::leaf_as_mut(leaf)) + } +} + +impl<'a, T: IsHierarchicalType, F: IsFormOf> Actual<'a, T, F> +where + F: LeafAsMutForm, + for<'l> T: IsHierarchicalType = >::Content<'l>>, +{ + pub(crate) fn as_mut<'r>(&'r mut self) -> Actual<'r, T, BeMut> { + match self.map_mut_with::() { + Ok(x) => x, + } + } +} diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs new file mode 100644 index 00000000..82819cb0 --- /dev/null +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -0,0 +1,48 @@ +use super::*; + +pub(crate) type QqqRef<'a, T> = Actual<'a, T, BeRef>; + +/// It can't be an argument because arguments must be owned in some way; +/// so that the drop glue can work properly (because they may come from +/// e.g. a reference counted Shared handle) +pub(crate) struct BeRef; +impl IsForm for BeRef {} + +impl IsHierarchicalForm for BeRef { + type Leaf<'a, T: IsValueLeaf> = &'a T; +} + +impl IsDynCompatibleForm for BeRef { + type DynLeaf<'a, T: 'static + ?Sized> = &'a T; +} + +impl IsDynMappableForm for BeRef { + fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> { + T::map_ref(leaf) + } +} + +pub(crate) struct ToRefMapper; + +impl RefLeafMapper for ToRefMapper { + type OutputForm = BeRef; + type ShortCircuit<'a> = Infallible; + + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>(leaf: &'r F::Leaf<'a, L>) -> Result<&'r L, Infallible> { + Ok(F::leaf_as_ref(leaf)) + } +} + +impl<'a, T: IsHierarchicalType, F: IsFormOf> Actual<'a, T, F> +where + F: LeafAsRefForm, + for<'l> T: IsHierarchicalType = >::Content<'l>>, +{ + pub(crate) fn as_ref<'r>(&'r self) -> Actual<'r, T, BeRef> { + match self.map_ref_with::() { + Ok(x) => x, + } + } +} diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 4e7d3934..f45ee341 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -29,6 +29,14 @@ pub(crate) trait IsHierarchicalType: IsType { structure: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; + fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + structure: &'r Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>>; + + fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + structure: &'r mut Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>>; + fn content_to_leaf_kind( content: &Self::Content<'_, F>, ) -> Self::LeafKind; @@ -51,6 +59,24 @@ pub(crate) trait LeafMapper { ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>>; } +pub(crate) trait RefLeafMapper { + type OutputForm: IsHierarchicalForm; + type ShortCircuit<'a>; + + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + leaf: &'r F::Leaf<'a, L>, + ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; +} + +pub(crate) trait MutLeafMapper { + type OutputForm: IsHierarchicalForm; + type ShortCircuit<'a>; + + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + leaf: &'r mut F::Leaf<'a, L>, + ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; +} + pub(crate) trait UpcastTo + IsFormOf>: IsType { fn upcast_to<'a>( content: >::Content<'a>, @@ -293,6 +319,22 @@ macro_rules! define_parent_type { }) } + fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + content: &'r Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>> { + Ok(match content { + $( $content::$variant(x) => $content::$variant(x.map_ref_with::()?), )* + }) + } + + fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + content: &'r mut Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>> { + Ok(match content { + $( $content::$variant(x) => $content::$variant(x.map_mut_with::()?), )* + }) + } + fn content_to_leaf_kind( content: &Self::Content<'_, F>, ) -> Self::LeafKind { @@ -401,6 +443,18 @@ macro_rules! define_leaf_type { M::map_leaf::<$content_type>(content) } + fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + content: &'r Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>> { + M::map_leaf::<$content_type>(content) + } + + fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + content: &'r mut Self::Content<'a, F>, + ) -> Result, M::ShortCircuit<'a>> { + M::map_leaf::<$content_type>(content) + } + fn content_to_leaf_kind( _content: &Self::Content<'_, F>, ) -> Self::LeafKind { diff --git a/src/internal_prelude.rs b/src/internal_prelude.rs index c02b5ca4..39f60afa 100644 --- a/src/internal_prelude.rs +++ b/src/internal_prelude.rs @@ -5,6 +5,7 @@ pub(crate) use std::{ borrow::Cow, cell::{Ref, RefCell, RefMut}, collections::{BTreeMap, HashMap, HashSet}, + convert::Infallible, fmt::Debug, iter, marker::PhantomData, From 6910a6014849b15f5af6d753cccbdb31b7bb64c6 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 4 Jan 2026 00:47:11 +0100 Subject: [PATCH 11/58] feat: Generate from_source_kind from macros --- plans/TODO.md | 12 ++- src/expressions/concepts/type_traits.rs | 32 ++++++++ .../type_resolution/value_kinds.rs | 73 ++++--------------- 3 files changed, 55 insertions(+), 62 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index b0e85cf4..c0c42940 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -207,17 +207,19 @@ First, read the @./2025-11-vision.md - [x] Separate Hierarchical and DynCompatible Forms - [x] Improved macro support - [x] Add source type name to type macro/s - - [ ] Generate value kinds from the macros + - [x] Generate value kinds from the macros - [x] Add (temporary) ability to link to TypeData and resolve methods from there - [x] Then implement all the macros - [x] And use that to generate ValueLeafKind from the new macros - - [ ] Find a way to generate `from_source_name` - ideally efficiently + - [x] Find a way to generate `from_source_name` - ideally efficiently - [ ] Add ability to implement IsIterable - [ ] `CastTarget` simply wraps `TypeKind` - [x] Create a `Ref` and a `Mut` form - [x] They won't implement `IsArgumentForm` - [x] Create mappers and suitably generic `as_ref()` and `as_mut()` methods on `Actual` - - [ ] Migrate method resolution to the trait macro properly + - [ ] Migrate method resolution to the trait macro properly + - [ ] Possibly separate own-type resolution `dyn TypeDetails` + - [ ] And property resolution `dyn TypeFeatureResolver` - [ ] Stage 1 of the form migration: - [ ] Replace `Owned` as type reference to `Actual<..>` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` @@ -231,10 +233,14 @@ First, read the @./2025-11-vision.md - [ ] Strip wrapper types like `StreamValue` - can just use `OutputStream` as content - [ ] Remove old definitions - [ ] Generate test over all value kinds which checks for: + - [ ] Maybe over TypeKinds with https://docs.rs/inventory/latest/inventory/ registered as a dev dependency + - [ ] Check for duplicate TypeKind registrations - [ ] Source type has no spaces and is lower case, and is invertible - [ ] Ancestor types agree with type kinds - [ ] Clear up all `TODO[concepts]` - [ ] Have variables store a `Referenceable` + - [ ] Separate methods and functions + - [ ] Add ability to add comments to types, methods/functions and operations ## Methods and closures diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index f45ee341..15ab9fc9 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -15,6 +15,7 @@ pub(crate) trait IsType: Sized { const ARTICLED_DISPLAY_NAME: &'static str; fn type_kind() -> TypeKind; + fn type_kind_from_source_name(name: &str) -> Option; } pub(crate) trait IsHierarchicalType: IsType { @@ -281,6 +282,19 @@ macro_rules! define_parent_type { fn type_kind() -> TypeKind { TypeKind::Parent(ParentTypeKind::$parent_kind($type_kind)) } + + #[inline] + fn type_kind_from_source_name(name: &str) -> Option { + if name == Self::SOURCE_TYPE_NAME { + return Some(Self::type_kind()); + } + $( + if let Some(type_kind) = <$variant_type as IsType>::type_kind_from_source_name(name) { + return Some(type_kind); + } + )* + None + } } impl MethodResolver for $type_def { @@ -431,6 +445,15 @@ macro_rules! define_leaf_type { fn type_kind() -> TypeKind { TypeKind::Leaf(ValueLeafKind::from($kind)) } + + #[inline] + fn type_kind_from_source_name(name: &str) -> Option { + if name == Self::SOURCE_TYPE_NAME { + Some(Self::type_kind()) + } else { + None + } + } } impl IsHierarchicalType for $type_def { @@ -573,6 +596,15 @@ macro_rules! define_dyn_type { fn type_kind() -> TypeKind { TypeKind::Dyn(DynTypeKind::$dyn_kind) } + + #[inline] + fn type_kind_from_source_name(name: &str) -> Option { + if name == Self::SOURCE_TYPE_NAME { + Some(Self::type_kind()) + } else { + None + } + } } impl IsDynType for $type_def { diff --git a/src/expressions/type_resolution/value_kinds.rs b/src/expressions/type_resolution/value_kinds.rs index 37dc376c..37f1138e 100644 --- a/src/expressions/type_resolution/value_kinds.rs +++ b/src/expressions/type_resolution/value_kinds.rs @@ -8,41 +8,6 @@ pub(crate) trait IsSpecificLeafKind: Copy + Into { fn method_resolver(&self) -> &'static dyn MethodResolver; } -impl ValueLeafKind { - pub(crate) fn from_source_name(name: &str) -> Option { - Some(match name { - "none" => ValueLeafKind::None(NoneKind), - "untyped_int" => ValueLeafKind::Integer(IntegerLeafKind::Untyped(UntypedIntegerKind)), - "i8" => ValueLeafKind::Integer(IntegerLeafKind::I8(I8Kind)), - "i16" => ValueLeafKind::Integer(IntegerLeafKind::I16(I16Kind)), - "i32" => ValueLeafKind::Integer(IntegerLeafKind::I32(I32Kind)), - "i64" => ValueLeafKind::Integer(IntegerLeafKind::I64(I64Kind)), - "i128" => ValueLeafKind::Integer(IntegerLeafKind::I128(I128Kind)), - "isize" => ValueLeafKind::Integer(IntegerLeafKind::Isize(IsizeKind)), - "u8" => ValueLeafKind::Integer(IntegerLeafKind::U8(U8Kind)), - "u16" => ValueLeafKind::Integer(IntegerLeafKind::U16(U16Kind)), - "u32" => ValueLeafKind::Integer(IntegerLeafKind::U32(U32Kind)), - "u64" => ValueLeafKind::Integer(IntegerLeafKind::U64(U64Kind)), - "u128" => ValueLeafKind::Integer(IntegerLeafKind::U128(U128Kind)), - "usize" => ValueLeafKind::Integer(IntegerLeafKind::Usize(UsizeKind)), - "untyped_float" => ValueLeafKind::Float(FloatLeafKind::Untyped(UntypedFloatKind)), - "f32" => ValueLeafKind::Float(FloatLeafKind::F32(F32Kind)), - "f64" => ValueLeafKind::Float(FloatLeafKind::F64(F64Kind)), - "bool" => ValueLeafKind::Bool(BoolKind), - "string" => ValueLeafKind::String(StringKind), - "unsupported_literal" => ValueLeafKind::UnsupportedLiteral(UnsupportedLiteralKind), - "char" => ValueLeafKind::Char(CharKind), - "array" => ValueLeafKind::Array(ArrayKind), - "object" => ValueLeafKind::Object(ObjectKind), - "stream" => ValueLeafKind::Stream(StreamKind), - "range" => ValueLeafKind::Range(RangeKind), - "iterator" => ValueLeafKind::Iterator(IteratorKind), - "parser" => ValueLeafKind::Parser(ParserKind), - _ => return None, - }) - } -} - impl ValueLeafKind { /// This should be true for types which users expect to have value /// semantics, but false for types which are expensive to clone or @@ -82,14 +47,13 @@ pub(crate) enum TypeKind { impl TypeKind { pub(crate) fn from_source_name(name: &str) -> Option { - if let Some(kind) = ValueLeafKind::from_source_name(name) { - return Some(TypeKind::Leaf(kind)); + // Parse Leaf and Parent TypeKinds + if let Some(tk) = ::type_kind_from_source_name(name) { + return Some(tk); } - if let Some(kind) = ParentTypeKind::from_source_name(name) { - return Some(TypeKind::Parent(kind)); - } - if let Some(kind) = DynTypeKind::from_source_name(name) { - return Some(TypeKind::Dyn(kind)); + // Parse Dyn TypeKinds + if let Some(dyn_type_kind) = DynTypeKind::from_source_name(name) { + return Some(TypeKind::Dyn(dyn_type_kind)); } None } @@ -110,15 +74,6 @@ pub(crate) enum ParentTypeKind { } impl ParentTypeKind { - pub(crate) fn from_source_name(name: &str) -> Option { - Some(match name { - ValueTypeKind::SOURCE_TYPE_NAME => ParentTypeKind::Value(ValueTypeKind), - IntegerTypeKind::SOURCE_TYPE_NAME => ParentTypeKind::Integer(IntegerTypeKind), - FloatTypeKind::SOURCE_TYPE_NAME => ParentTypeKind::Float(FloatTypeKind), - _ => return None, - }) - } - pub(crate) fn source_name(&self) -> &'static str { match self { ParentTypeKind::Value(ValueTypeKind) => ValueTypeKind::SOURCE_TYPE_NAME, @@ -141,22 +96,22 @@ pub(crate) enum DynTypeKind { } impl DynTypeKind { - pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { - match self { - DynTypeKind::Iterable => &IterableTypeData, - } - } - pub(crate) fn from_source_name(name: &str) -> Option { match name { - "iterable" => Some(DynTypeKind::Iterable), + IterableType::SOURCE_TYPE_NAME => Some(DynTypeKind::Iterable), _ => None, } } pub(crate) fn source_name(&self) -> &'static str { match self { - DynTypeKind::Iterable => "iterable", + DynTypeKind::Iterable => IterableType::SOURCE_TYPE_NAME, + } + } + + pub(crate) fn method_resolver(&self) -> &'static dyn MethodResolver { + match self { + DynTypeKind::Iterable => &IterableTypeData, } } } From a19fc0689ed39fefc7436af63e7aa222204a1a61 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 4 Jan 2026 00:50:36 +0100 Subject: [PATCH 12/58] tweak: Tweak inlining --- src/expressions/concepts/type_traits.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 15ab9fc9..3df240d5 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -283,7 +283,7 @@ macro_rules! define_parent_type { TypeKind::Parent(ParentTypeKind::$parent_kind($type_kind)) } - #[inline] + #[inline(always)] fn type_kind_from_source_name(name: &str) -> Option { if name == Self::SOURCE_TYPE_NAME { return Some(Self::type_kind()); @@ -446,7 +446,10 @@ macro_rules! define_leaf_type { TypeKind::Leaf(ValueLeafKind::from($kind)) } - #[inline] + // This is called for every leaf in a row as part of parsing + // Use inline(always) to avoid function call overhead, even in Debug builds + // which are used when macros are run during development + #[inline(always)] fn type_kind_from_source_name(name: &str) -> Option { if name == Self::SOURCE_TYPE_NAME { Some(Self::type_kind()) @@ -597,7 +600,10 @@ macro_rules! define_dyn_type { TypeKind::Dyn(DynTypeKind::$dyn_kind) } - #[inline] + // This is called for every leaf in a row as part of parsing + // Use inline(always) to avoid function call overhead, even in Debug builds + // which are used when macros are run during development + #[inline(always)] fn type_kind_from_source_name(name: &str) -> Option { if name == Self::SOURCE_TYPE_NAME { Some(Self::type_kind()) From 66ff59d6303ecc5d628b39db5d895c3e7e667416 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 4 Jan 2026 00:55:32 +0100 Subject: [PATCH 13/58] refactor: Rename value_kinds.rs to type_kinds.rs --- src/expressions/type_resolution/mod.rs | 4 ++-- .../type_resolution/{value_kinds.rs => type_kinds.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/expressions/type_resolution/{value_kinds.rs => type_kinds.rs} (100%) diff --git a/src/expressions/type_resolution/mod.rs b/src/expressions/type_resolution/mod.rs index f8e22e6c..3e86854f 100644 --- a/src/expressions/type_resolution/mod.rs +++ b/src/expressions/type_resolution/mod.rs @@ -4,10 +4,10 @@ mod arguments; mod interface_macros; mod outputs; mod type_data; -mod value_kinds; +mod type_kinds; pub(crate) use arguments::*; pub(crate) use interface_macros::*; pub(crate) use outputs::*; pub(crate) use type_data::*; -pub(crate) use value_kinds::*; +pub(crate) use type_kinds::*; diff --git a/src/expressions/type_resolution/value_kinds.rs b/src/expressions/type_resolution/type_kinds.rs similarity index 100% rename from src/expressions/type_resolution/value_kinds.rs rename to src/expressions/type_resolution/type_kinds.rs From 826fa630ac22758824ba193a8bae239fab9bf78b Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 4 Jan 2026 16:14:55 +0100 Subject: [PATCH 14/58] refactor: CastTarget wraps ValueLeafKind --- benches/basic.rs | 2 +- plans/TODO.md | 7 +- src/expressions/concepts/type_traits.rs | 4 +- src/expressions/evaluation/value_frames.rs | 2 +- src/expressions/operations.rs | 90 +++++++------------ src/expressions/type_resolution/type_kinds.rs | 22 +++-- src/expressions/values/array.rs | 14 ++- src/expressions/values/boolean.rs | 32 +++---- src/expressions/values/character.rs | 32 +++---- src/expressions/values/float_subtypes.rs | 36 ++++---- src/expressions/values/float_untyped.rs | 36 ++++---- src/expressions/values/integer_subtypes.rs | 38 ++++---- src/expressions/values/integer_untyped.rs | 36 ++++---- src/expressions/values/iterable.rs | 11 +++ src/expressions/values/iterator.rs | 12 ++- src/expressions/values/stream.rs | 11 +-- src/expressions/values/string.rs | 4 +- src/expressions/values/value.rs | 6 +- .../expressions/cast_to_float.rs | 7 ++ .../expressions/cast_to_float.stderr | 5 ++ .../expressions/cast_to_int.rs | 7 ++ .../expressions/cast_to_int.stderr | 5 ++ .../expressions/cast_to_unknown_type.rs | 7 ++ .../expressions/cast_to_unknown_type.stderr | 5 ++ .../expressions/large_range_to_string.stderr | 6 +- .../expressions/untyped_integer_overflow.rs | 2 +- .../untyped_integer_overflow.stderr | 6 +- tests/expressions.rs | 6 +- tests/operations.rs | 8 +- 29 files changed, 241 insertions(+), 218 deletions(-) create mode 100644 tests/compilation_failures/expressions/cast_to_float.rs create mode 100644 tests/compilation_failures/expressions/cast_to_float.stderr create mode 100644 tests/compilation_failures/expressions/cast_to_int.rs create mode 100644 tests/compilation_failures/expressions/cast_to_int.stderr create mode 100644 tests/compilation_failures/expressions/cast_to_unknown_type.rs create mode 100644 tests/compilation_failures/expressions/cast_to_unknown_type.stderr diff --git a/benches/basic.rs b/benches/basic.rs index eda5a1ef..2f83c358 100644 --- a/benches/basic.rs +++ b/benches/basic.rs @@ -26,7 +26,7 @@ fn main() { output }); benchmark!("Lots of casts", { - 0 as u32 as int as u8 as char as string as stream + 0 as u32 as untyped_int as u8 as char as string as stream }); benchmark!("Simple tuple impls", { for N in 0..=10 { diff --git a/plans/TODO.md b/plans/TODO.md index c0c42940..654e6c44 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -213,7 +213,7 @@ First, read the @./2025-11-vision.md - [x] And use that to generate ValueLeafKind from the new macros - [x] Find a way to generate `from_source_name` - ideally efficiently - [ ] Add ability to implement IsIterable - - [ ] `CastTarget` simply wraps `TypeKind` + - [x] `CastTarget` simply wraps `TypeKind` - [x] Create a `Ref` and a `Mut` form - [x] They won't implement `IsArgumentForm` - [x] Create mappers and suitably generic `as_ref()` and `as_mut()` methods on `Actual` @@ -226,6 +226,9 @@ First, read the @./2025-11-vision.md - [ ] And similarly for other values... - [ ] Stage 2 of the form migration: - [ ] Migrate `Shared`, `Mutable`, `Assignee` + - [ ] Stage 3 + - [ ] Migtate `CopyOnWrite` .. + - [ ] FInish migrating to new Argument/Returned resolution and delete old cold - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): - [ ] CopyOnWrite, Shared => CopyOnWrite x2, Owned => CopyOnWrite - [ ] LateBound, Tons of conversions into it @@ -551,7 +554,7 @@ Also: - [ ] Better handling of `configure_preinterpret`: * Move `None.configure_preinterpret` to `preinterpret::set_iteration_limit(..)` - [ ] CastTarget revision: - * The `as int` operator is not supported for string values + * The `as untyped_int` operator is not supported for string values * The `as char` operator is not supported for untyped integer values * Add casts of any integer to char, via `char::from_u32(u32::try_from(x))` * Should we remove/replace any CastTargets? diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 3df240d5..75d9dea6 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -376,7 +376,9 @@ macro_rules! define_parent_type { $type_kind_vis struct $type_kind; impl $type_kind { - pub(crate) const SOURCE_TYPE_NAME: &'static str = $source_type_name; + pub(crate) fn source_type_name(&self) -> &'static str { + $source_type_name + } pub(crate) fn method_resolver(&self) -> &'static dyn MethodResolver { &$type_def diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 3c480d5d..ba9c42f7 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -806,7 +806,7 @@ impl EvaluationFrame for UnaryOperationBuilder { } self.operation.type_err(format!( "The {} operator is not supported for {}", - self.operation.symbolic_description(), + self.operation, operand.articled_kind(), )) } diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index f04f3438..d36d4d77 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -1,3 +1,5 @@ +use std::fmt::{Display, Formatter}; + use super::*; pub(crate) trait Operation: HasSpanRange { @@ -60,9 +62,7 @@ impl UnaryOperation { target_ident: Ident, ) -> ParseResult { let target = TypeIdent::from_ident(&target_ident)?; - let target = CastTarget::from_source_type(target).ok_or_else(|| { - target_ident.parse_error("This type is not supported in cast expressions") - })?; + let target = CastTarget::from_source_type(target)?; Ok(Self::Cast { as_token, @@ -94,7 +94,7 @@ impl UnaryOperation { .ok_or_else(|| { self.type_error(format!( "The {} operator is not supported for {} operand", - self.symbolic_description(), + self, input.articled_kind(), )) })?; @@ -106,67 +106,43 @@ impl UnaryOperation { } #[derive(Copy, Clone)] -pub(crate) enum CastTarget { - Integer(IntegerLeafKind), - Float(FloatLeafKind), - Boolean, - String, - Char, - Stream, -} +pub(crate) struct CastTarget(pub(crate) ValueLeafKind); impl CastTarget { - fn from_source_type(s: TypeIdent) -> Option { - Some(match s.kind { - TypeKind::Parent(ParentTypeKind::Integer(_)) => { - CastTarget::Integer(IntegerLeafKind::Untyped(UntypedIntegerKind)) - } - TypeKind::Leaf(ValueLeafKind::Integer(kind)) => CastTarget::Integer(kind), - TypeKind::Parent(ParentTypeKind::Float(_)) => { - CastTarget::Float(FloatLeafKind::Untyped(UntypedFloatKind)) - } - TypeKind::Leaf(ValueLeafKind::Float(kind)) => CastTarget::Float(kind), - TypeKind::Leaf(ValueLeafKind::Bool(_)) => CastTarget::Boolean, - TypeKind::Leaf(ValueLeafKind::String(_)) => CastTarget::String, - TypeKind::Leaf(ValueLeafKind::Char(_)) => CastTarget::Char, - TypeKind::Leaf(ValueLeafKind::Stream(_)) => CastTarget::Stream, - _ => return None, - }) + fn from_source_type(s: TypeIdent) -> ParseResult { + match s.kind { + TypeKind::Parent(ParentTypeKind::Integer(_)) => s.parse_err(format!( + "This type is not supported in cast expressions. Perhaps you want 'as {}'?", + UntypedIntegerKind.source_type_name() + )), + TypeKind::Parent(ParentTypeKind::Float(_)) => s.parse_err(format!( + "This type is not supported in cast expressions. Perhaps you want 'as {}'?", + UntypedFloatKind.source_type_name() + )), + TypeKind::Leaf(leaf_kind) => Ok(CastTarget(leaf_kind)), + _ => s.parse_err("This type is not supported in cast expressions"), + } } - // TODO[concepts]: Improve this / align with the type name - fn symbolic_description(&self) -> &'static str { - match self { - CastTarget::Integer(IntegerLeafKind::Untyped(_)) => "as int", - CastTarget::Integer(IntegerLeafKind::U8(_)) => "as u8", - CastTarget::Integer(IntegerLeafKind::U16(_)) => "as u16", - CastTarget::Integer(IntegerLeafKind::U32(_)) => "as u32", - CastTarget::Integer(IntegerLeafKind::U64(_)) => "as u64", - CastTarget::Integer(IntegerLeafKind::U128(_)) => "as u128", - CastTarget::Integer(IntegerLeafKind::Usize(_)) => "as usize", - CastTarget::Integer(IntegerLeafKind::I8(_)) => "as i8", - CastTarget::Integer(IntegerLeafKind::I16(_)) => "as i16", - CastTarget::Integer(IntegerLeafKind::I32(_)) => "as i32", - CastTarget::Integer(IntegerLeafKind::I64(_)) => "as i64", - CastTarget::Integer(IntegerLeafKind::I128(_)) => "as i128", - CastTarget::Integer(IntegerLeafKind::Isize(_)) => "as isize", - CastTarget::Float(FloatLeafKind::Untyped(_)) => "as float", - CastTarget::Float(FloatLeafKind::F32(_)) => "as f32", - CastTarget::Float(FloatLeafKind::F64(_)) => "as f64", - CastTarget::Boolean => "as bool", - CastTarget::String => "as string", - CastTarget::Char => "as char", - CastTarget::Stream => "as stream", - } + /// Denotes that this cast target for a singleton iterable + /// (e.g. array or stream) + pub(crate) fn is_singleton_target(&self) -> bool { + matches!( + self.0, + ValueLeafKind::Bool(_) + | ValueLeafKind::Char(_) + | ValueLeafKind::Integer(_) + | ValueLeafKind::Float(_) + ) } } -impl Operation for UnaryOperation { - fn symbolic_description(&self) -> &'static str { +impl Display for UnaryOperation { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - UnaryOperation::Neg { .. } => "-", - UnaryOperation::Not { .. } => "!", - UnaryOperation::Cast { target, .. } => target.symbolic_description(), + UnaryOperation::Neg { .. } => write!(f, "-"), + UnaryOperation::Not { .. } => write!(f, "!"), + UnaryOperation::Cast { target, .. } => write!(f, "as {}", target.0.source_type_name()), } } } diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs index 37f1138e..c6a30db4 100644 --- a/src/expressions/type_resolution/type_kinds.rs +++ b/src/expressions/type_resolution/type_kinds.rs @@ -76,9 +76,9 @@ pub(crate) enum ParentTypeKind { impl ParentTypeKind { pub(crate) fn source_name(&self) -> &'static str { match self { - ParentTypeKind::Value(ValueTypeKind) => ValueTypeKind::SOURCE_TYPE_NAME, - ParentTypeKind::Integer(IntegerTypeKind) => IntegerTypeKind::SOURCE_TYPE_NAME, - ParentTypeKind::Float(FloatTypeKind) => FloatTypeKind::SOURCE_TYPE_NAME, + ParentTypeKind::Value(x) => x.source_type_name(), + ParentTypeKind::Integer(x) => x.source_type_name(), + ParentTypeKind::Float(x) => x.source_type_name(), } } @@ -132,11 +132,11 @@ impl TypeIdent { let error_message = match TypeKind::from_source_name(&lower_case_name) { Some(_) => format!("Expected '{}'", lower_case_name), None => match lower_case_name.as_str() { - "integer" => "Expected 'int'".to_string(), - "str" => "Expected 'string'".to_string(), - "character" => "Expected 'char'".to_string(), - "list" => "Expected 'array'".to_string(), - "obj" => "Expected 'object'".to_string(), + "integer" => format!("Expected '{}'", IntegerType::SOURCE_TYPE_NAME), + "str" => format!("Expected '{}'", StringType::SOURCE_TYPE_NAME), + "character" => format!("Expected '{}'", CharType::SOURCE_TYPE_NAME), + "list" => format!("Expected '{}'", ArrayType::SOURCE_TYPE_NAME), + "obj" => format!("Expected '{}'", ObjectType::SOURCE_TYPE_NAME), _ => format!("Unknown type '{}'", name), }, }; @@ -147,6 +147,12 @@ impl TypeIdent { } } +impl HasSpan for TypeIdent { + fn span(&self) -> Span { + self.span + } +} + impl ParseSource for TypeIdent { fn parse(input: SourceParser) -> ParseResult { Self::from_ident(&input.parse()?) diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 9b7fe5e2..9067e139 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -197,7 +197,7 @@ define_interface! { } } pub(crate) mod unary_operations { - [context] fn cast_to_numeric(Spanned(this, span): Spanned>) -> ExecutionResult { + [context] fn cast_singleton_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { let mut this = this.into_inner(); let length = this.items.len(); if length == 1 { @@ -224,13 +224,11 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } | UnaryOperation::Not { .. } => return None, - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Boolean - | CastTarget::Char - | CastTarget::Integer(_) - | CastTarget::Float(_) => unary_definitions::cast_to_numeric(), - _ => return None, - }, + UnaryOperation::Cast { target, .. } => if target.is_singleton_target() { + unary_definitions::cast_singleton_to_value() + } else { + return None; + } }) } diff --git a/src/expressions/values/boolean.rs b/src/expressions/values/boolean.rs index 28cb9cf0..758998e9 100644 --- a/src/expressions/values/boolean.rs +++ b/src/expressions/values/boolean.rs @@ -200,22 +200,22 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Not { .. } => unary_definitions::not(), - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - CastTarget::Boolean => unary_definitions::cast_to_boolean(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + ValueLeafKind::Bool(_) => unary_definitions::cast_to_boolean(), + ValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, diff --git a/src/expressions/values/character.rs b/src/expressions/values/character.rs index 9f05f680..9e1c9972 100644 --- a/src/expressions/values/character.rs +++ b/src/expressions/values/character.rs @@ -169,22 +169,22 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - CastTarget::Char => unary_definitions::cast_to_char(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + ValueLeafKind::Char(_) => unary_definitions::cast_to_char(), + ValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 04bbf4fd..64b310cb 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -89,24 +89,24 @@ macro_rules! impl_float_operations { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + ValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + ValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + ValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + ValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, } _ => return None, diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index b76bffe1..0942695b 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -161,24 +161,24 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + ValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + ValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + ValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + ValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index a4a2520b..35428e5e 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -109,30 +109,30 @@ macro_rules! impl_int_operations { unary_definitions::neg() } )? - UnaryOperation::Cast { target, .. } => match target { + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { $( - CastTarget::Char => { + ValueLeafKind::Char(_) => { ignore_all!($char_cast); // Only include for types with CharCast unary_definitions::cast_to_char() } )? - CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), - CastTarget::String => unary_definitions::cast_to_string(), + ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + ValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + ValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + ValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + ValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, } _ => return None, diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index 90fa21d1..740208a0 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -217,24 +217,24 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + ValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + ValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + ValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + ValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index b1a55c71..4de4fce0 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -85,9 +85,20 @@ define_interface! { } } pub(crate) mod unary_operations { + fn cast_into_iterator(this: IterableValue) -> ExecutionResult { + this.into_iterator() + } } pub(crate) mod binary_operations {} interface_items { + fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { + Some(match operation { + UnaryOperation::Cast { target: CastTarget(ValueLeafKind::Iterator(_)), .. } =>{ + unary_definitions::cast_into_iterator() + } + _ => return None, + }) + } } } } diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index df267d18..57f97fd7 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -332,13 +332,11 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } | UnaryOperation::Not { .. } => return None, - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Boolean - | CastTarget::Char - | CastTarget::Integer(_) - | CastTarget::Float(_) => unary_definitions::cast_singleton_to_value(), - _ => return None, - }, + UnaryOperation::Cast { target, .. } => if target.is_singleton_target() { + unary_definitions::cast_singleton_to_value() + } else { + return None; + } }) } } diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index 78f7f4a1..07e9e0b5 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -271,7 +271,7 @@ define_interface! { } } pub(crate) mod unary_operations { - [context] fn cast_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { + [context] fn cast_coerced_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { let this = this.into_inner(); let coerced = this.value.coerce_into_value(); if let Value::Stream(_) = &coerced { @@ -294,14 +294,7 @@ define_interface! { interface_items { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { - UnaryOperation::Cast { - target: - CastTarget::Boolean - | CastTarget::Char - | CastTarget::Integer(_) - | CastTarget::Float(_), - .. - } => unary_definitions::cast_to_value(), + UnaryOperation::Cast { target, .. } if target.is_singleton_target() => unary_definitions::cast_coerced_to_value(), _ => return None, }) } diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index 2a49270a..c7a13c90 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -211,8 +211,8 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } | UnaryOperation::Not { .. } => return None, - UnaryOperation::Cast { target, .. } => match target { - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + ValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, }) diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index ca7aa6ed..1d677ad4 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -196,9 +196,9 @@ define_interface! { interface_items { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { - UnaryOperation::Cast { target, .. } => match target { - CastTarget::String => unary_definitions::cast_to_string(), - CastTarget::Stream => unary_definitions::cast_to_stream(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + ValueLeafKind::String(_) => unary_definitions::cast_to_string(), + ValueLeafKind::Stream(_) => unary_definitions::cast_to_stream(), _ => return None, }, _ => return None, diff --git a/tests/compilation_failures/expressions/cast_to_float.rs b/tests/compilation_failures/expressions/cast_to_float.rs new file mode 100644 index 00000000..3342659c --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_float.rs @@ -0,0 +1,7 @@ +use preinterpret::*; + +fn main() { + let _ = run!{ + 0u8 as float + }; +} \ No newline at end of file diff --git a/tests/compilation_failures/expressions/cast_to_float.stderr b/tests/compilation_failures/expressions/cast_to_float.stderr new file mode 100644 index 00000000..7503700e --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_float.stderr @@ -0,0 +1,5 @@ +error: This type is not supported in cast expressions. Perhaps you want 'as untyped_float'? + --> tests/compilation_failures/expressions/cast_to_float.rs:5:16 + | +5 | 0u8 as float + | ^^^^^ diff --git a/tests/compilation_failures/expressions/cast_to_int.rs b/tests/compilation_failures/expressions/cast_to_int.rs new file mode 100644 index 00000000..c5f6ad90 --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_int.rs @@ -0,0 +1,7 @@ +use preinterpret::*; + +fn main() { + let _ = run!{ + 0u8 as int + }; +} \ No newline at end of file diff --git a/tests/compilation_failures/expressions/cast_to_int.stderr b/tests/compilation_failures/expressions/cast_to_int.stderr new file mode 100644 index 00000000..55810f00 --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_int.stderr @@ -0,0 +1,5 @@ +error: This type is not supported in cast expressions. Perhaps you want 'as untyped_int'? + --> tests/compilation_failures/expressions/cast_to_int.rs:5:16 + | +5 | 0u8 as int + | ^^^ diff --git a/tests/compilation_failures/expressions/cast_to_unknown_type.rs b/tests/compilation_failures/expressions/cast_to_unknown_type.rs new file mode 100644 index 00000000..523675cb --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_unknown_type.rs @@ -0,0 +1,7 @@ +use preinterpret::*; + +fn main() { + let _ = run!{ + 0u8 as nonexistent_type + }; +} \ No newline at end of file diff --git a/tests/compilation_failures/expressions/cast_to_unknown_type.stderr b/tests/compilation_failures/expressions/cast_to_unknown_type.stderr new file mode 100644 index 00000000..ca49fb70 --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_unknown_type.stderr @@ -0,0 +1,5 @@ +error: Unknown type 'nonexistent_type' + --> tests/compilation_failures/expressions/cast_to_unknown_type.rs:5:16 + | +5 | 0u8 as nonexistent_type + | ^^^^^^^^^^^^^^^^ diff --git a/tests/compilation_failures/expressions/large_range_to_string.stderr b/tests/compilation_failures/expressions/large_range_to_string.stderr index 54e7f415..1e971e34 100644 --- a/tests/compilation_failures/expressions/large_range_to_string.stderr +++ b/tests/compilation_failures/expressions/large_range_to_string.stderr @@ -1,5 +1,5 @@ -error: This type is not supported in cast expressions - --> tests/compilation_failures/expressions/large_range_to_string.rs:5:25 +error: To protect against infinite loops, only a maximum of 1000 items can be output to a string from an iterator. You can use .to_vec() to avoid this limit. This can't currently be reconfigured with the iteration limit. + --> tests/compilation_failures/expressions/large_range_to_string.rs:5:11 | 5 | #((0..10000) as iterator as string) - | ^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/compilation_failures/expressions/untyped_integer_overflow.rs b/tests/compilation_failures/expressions/untyped_integer_overflow.rs index 50b58009..48ceb487 100644 --- a/tests/compilation_failures/expressions/untyped_integer_overflow.rs +++ b/tests/compilation_failures/expressions/untyped_integer_overflow.rs @@ -1,5 +1,5 @@ use preinterpret::*; fn main() { - let _ = run!(i128::MAX as int + 1); + let _ = run!(i128::MAX as untyped_int + 1); } \ No newline at end of file diff --git a/tests/compilation_failures/expressions/untyped_integer_overflow.stderr b/tests/compilation_failures/expressions/untyped_integer_overflow.stderr index 6418f54d..2a1e29b4 100644 --- a/tests/compilation_failures/expressions/untyped_integer_overflow.stderr +++ b/tests/compilation_failures/expressions/untyped_integer_overflow.stderr @@ -1,5 +1,5 @@ error: The untyped integer operation 170141183460469231731687303715884105727 + 1 overflowed in i128 space - --> tests/compilation_failures/expressions/untyped_integer_overflow.rs:4:35 + --> tests/compilation_failures/expressions/untyped_integer_overflow.rs:4:43 | -4 | let _ = run!(i128::MAX as int + 1); - | ^ +4 | let _ = run!(i128::MAX as untyped_int + 1); + | ^ diff --git a/tests/expressions.rs b/tests/expressions.rs index 2e8b64ce..750dfff9 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -32,8 +32,8 @@ fn test_basic_evaluate_works() { assert!(run!(true || false && false)); // The && has priority assert!(run!(true | false & false)); // The & has priority assert_eq!(run!(true as u32 + 2), 3); - assert_eq!(run!(3.57 as int + 1), 4u32); - assert_eq!(run!(3.57 as int + 1), 4u64); + assert_eq!(run!(3.57 as untyped_int + 1), 4u32); + assert_eq!(run!(3.57 as untyped_int + 1), 4u64); assert_eq!(run!(0b1000 & 0b1101), 0b1000); assert_eq!(run!(0b1000 | 0b1101), 0b1101); assert_eq!(run!(0b1000 ^ 0b1101), 0b101); @@ -53,7 +53,7 @@ fn test_basic_evaluate_works() { ), "%[5 + 2 = 7]" ); - assert_eq!(run!(1 + (1..2) as int), 2); + assert_eq!(run!(1 + (1..2) as untyped_int), 2); assert!(!run!("hello" == "world")); assert!(run!("hello" == "hello")); assert!(run!('A' as u8 == 65)); diff --git a/tests/operations.rs b/tests/operations.rs index a3980310..c7968932 100644 --- a/tests/operations.rs +++ b/tests/operations.rs @@ -1541,21 +1541,21 @@ mod cast_operations { assert_eq!(run!(256u32 as u8), 0u8); // overflow wraps assert_eq!(run!(-1i32 as u32), u32::MAX); assert_eq!(run!(5 as i32), 5i32); - assert_eq!(run!(5 as int), 5); + assert_eq!(run!(5 as untyped_int), 5); } #[test] fn cast_integer_to_float() { assert_eq!(run!(5u32 as f32), 5.0f32); assert_eq!(run!(5i32 as f64), 5.0f64); - assert_eq!(run!(5 as float), 5.0); + assert_eq!(run!(5 as untyped_float), 5.0); } #[test] fn cast_float_to_integer() { assert_eq!(run!(5.7f32 as i32), 5i32); assert_eq!(run!(5.7f64 as u32), 5u32); - assert_eq!(run!(5.7 as int), 5); + assert_eq!(run!(5.7 as untyped_int), 5); } #[test] @@ -1577,7 +1577,7 @@ mod cast_operations { fn cast_char_to_integer() { assert_eq!(run!('A' as u8), 65u8); assert_eq!(run!('A' as u32), 65u32); - assert_eq!(run!('A' as int), 65); + assert_eq!(run!('A' as untyped_int), 65); } #[test] From 5ee67419c53bfe391ec79b43c131c5043a11bc8a Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 4 Jan 2026 17:11:23 +0100 Subject: [PATCH 15/58] feat: Dyn types can be implemented in leaf type macros --- plans/TODO.md | 7 +-- src/expressions/concepts/dyn_impls.rs | 14 ----- src/expressions/concepts/mod.rs | 2 - src/expressions/concepts/type_traits.rs | 38 ++++++++++++- src/expressions/type_resolution/type_kinds.rs | 4 +- src/expressions/values/array.rs | 11 ++++ src/expressions/values/boolean.rs | 1 + src/expressions/values/character.rs | 1 + src/expressions/values/float_subtypes.rs | 1 + src/expressions/values/float_untyped.rs | 1 + src/expressions/values/integer_subtypes.rs | 1 + src/expressions/values/integer_untyped.rs | 1 + src/expressions/values/iterable.rs | 53 ++++++++++++------- src/expressions/values/iterator.rs | 22 +++++--- src/expressions/values/none.rs | 1 + src/expressions/values/object.rs | 11 ++++ src/expressions/values/parser.rs | 1 + src/expressions/values/range.rs | 11 ++++ src/expressions/values/stream.rs | 11 ++++ src/expressions/values/string.rs | 13 +++++ src/expressions/values/unsupported_literal.rs | 1 + 21 files changed, 159 insertions(+), 47 deletions(-) delete mode 100644 src/expressions/concepts/dyn_impls.rs diff --git a/plans/TODO.md b/plans/TODO.md index 654e6c44..f9278a2a 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -212,7 +212,7 @@ First, read the @./2025-11-vision.md - [x] Then implement all the macros - [x] And use that to generate ValueLeafKind from the new macros - [x] Find a way to generate `from_source_name` - ideally efficiently - - [ ] Add ability to implement IsIterable + - [x] Add ability to implement IsIterable - [x] `CastTarget` simply wraps `TypeKind` - [x] Create a `Ref` and a `Mut` form - [x] They won't implement `IsArgumentForm` @@ -220,6 +220,7 @@ First, read the @./2025-11-vision.md - [ ] Migrate method resolution to the trait macro properly - [ ] Possibly separate own-type resolution `dyn TypeDetails` - [ ] And property resolution `dyn TypeFeatureResolver` + - [ ] Allow dyns to be injected into resolution flow - [ ] Stage 1 of the form migration: - [ ] Replace `Owned` as type reference to `Actual<..>` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` @@ -227,8 +228,8 @@ First, read the @./2025-11-vision.md - [ ] Stage 2 of the form migration: - [ ] Migrate `Shared`, `Mutable`, `Assignee` - [ ] Stage 3 - - [ ] Migtate `CopyOnWrite` .. - - [ ] FInish migrating to new Argument/Returned resolution and delete old cold + - [ ] Migrate `CopyOnWrite` .. + - [ ] Finish migrating to new Argument/Returned resolution and delete old code - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): - [ ] CopyOnWrite, Shared => CopyOnWrite x2, Owned => CopyOnWrite - [ ] LateBound, Tons of conversions into it diff --git a/src/expressions/concepts/dyn_impls.rs b/src/expressions/concepts/dyn_impls.rs deleted file mode 100644 index 986dbc3e..00000000 --- a/src/expressions/concepts/dyn_impls.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::*; - -pub(crate) trait IsIterable: 'static { - fn into_iterator(self: Box) -> ExecutionResult; - fn len(&self, error_span_range: SpanRange) -> ExecutionResult; -} - -define_dyn_type!( - pub(crate) IterableType, - content: dyn IsIterable, - dyn_kind: DynTypeKind::Iterable, - type_name: "iterable", - articled_display_name: "an iterable (e.g. array, list, etc.)", -); diff --git a/src/expressions/concepts/mod.rs b/src/expressions/concepts/mod.rs index 26750a00..6ec59a94 100644 --- a/src/expressions/concepts/mod.rs +++ b/src/expressions/concepts/mod.rs @@ -2,13 +2,11 @@ use super::*; mod actual; -mod dyn_impls; mod form; mod forms; mod type_traits; pub(crate) use actual::*; -pub(crate) use dyn_impls::*; pub(crate) use form::*; pub(crate) use forms::*; pub(crate) use type_traits::*; diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 75d9dea6..b7d7f2da 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -427,6 +427,22 @@ macro_rules! define_parent_type { pub(crate) use define_parent_type; +macro_rules! impl_fallback_is_iterable { + ({IsIterable $($haystack:tt)*} for $content_type:ty) => { + // Already implemented, ignoring + }; + ({$discard:tt $($haystack:tt)*} for $content_type:ty) => { + // Recurse to check the rest + impl_is_iterable_if_missing!({$($haystack)*} for $content_type) + }; + ({} for $content_type:ty) => { + // Implement fallback + impl CastDyn for $content_type {} + }; +} + +pub(crate) use impl_fallback_is_iterable; + macro_rules! define_leaf_type { ( $type_def_vis:vis $type_def:ident => $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*, @@ -435,6 +451,9 @@ macro_rules! define_leaf_type { type_name: $source_type_name:literal, articled_display_name: $articled_display_name:literal, temp_type_data: $type_data:ident, + dyn_impls: { + $(impl $dyn_trait:ident { $($dyn_trait_impl:tt)* })* + }, ) => { $type_def_vis struct $type_def; @@ -570,7 +589,24 @@ macro_rules! define_leaf_type { } impl IsValueLeaf for $content_type {} - impl CastDyn for $content_type {} + + $( + impl $dyn_trait for $content_type { + $($dyn_trait_impl)* + } + impl CastDyn for $content_type { + fn map_boxed(self: Box) -> Option> { + Some(self) + } + fn map_ref(&self) -> Option<&dyn $dyn_trait> { + Some(self) + } + fn map_mut(&mut self) -> Option<&mut dyn $dyn_trait> { + Some(self) + } + } + )* + impl_fallback_is_iterable!({$($dyn_trait)*} for $content_type); impl_ancestor_chain_conversions!( $type_def => $parent($parent_content :: $parent_variant) $(=> $ancestor)* diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs index c6a30db4..a32e98be 100644 --- a/src/expressions/type_resolution/type_kinds.rs +++ b/src/expressions/type_resolution/type_kinds.rs @@ -135,7 +135,9 @@ impl TypeIdent { "integer" => format!("Expected '{}'", IntegerType::SOURCE_TYPE_NAME), "str" => format!("Expected '{}'", StringType::SOURCE_TYPE_NAME), "character" => format!("Expected '{}'", CharType::SOURCE_TYPE_NAME), - "list" => format!("Expected '{}'", ArrayType::SOURCE_TYPE_NAME), + "list" | "vec" | "tuple" => { + format!("Expected '{}'", ArrayType::SOURCE_TYPE_NAME) + } "obj" => format!("Expected '{}'", ObjectType::SOURCE_TYPE_NAME), _ => format!("Unknown type '{}'", name), }, diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 9067e139..25a86401 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -7,6 +7,17 @@ define_leaf_type! { type_name: "array", articled_display_name: "an array", temp_type_data: ArrayTypeData, + dyn_impls: { + impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(IteratorValue::new_for_array(*self)) + } + + fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + Ok(self.items.len()) + } + } + }, } #[derive(Clone)] diff --git a/src/expressions/values/boolean.rs b/src/expressions/values/boolean.rs index 758998e9..1646ae1c 100644 --- a/src/expressions/values/boolean.rs +++ b/src/expressions/values/boolean.rs @@ -9,6 +9,7 @@ define_leaf_type! { type_name: "bool", articled_display_name: "a bool", temp_type_data: BooleanTypeData, + dyn_impls: {}, } #[derive(Clone)] diff --git a/src/expressions/values/character.rs b/src/expressions/values/character.rs index 9e1c9972..3e447b25 100644 --- a/src/expressions/values/character.rs +++ b/src/expressions/values/character.rs @@ -7,6 +7,7 @@ define_leaf_type! { type_name: "char", articled_display_name: "a char", temp_type_data: CharTypeData, + dyn_impls: {}, } #[derive(Clone)] diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 64b310cb..f66193cd 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -164,6 +164,7 @@ macro_rules! impl_resolvable_float_subtype { type_name: $type_name, articled_display_name: $articled_display_name, temp_type_data: $value_type, + dyn_impls: {}, } impl ResolvableArgumentTarget for $type { diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 0942695b..2368f0a5 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -7,6 +7,7 @@ define_leaf_type! { type_name: "untyped_float", articled_display_name: "an untyped float", temp_type_data: UntypedFloatTypeData, + dyn_impls: {}, } #[derive(Copy, Clone)] diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 35428e5e..4e72b373 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -197,6 +197,7 @@ macro_rules! impl_resolvable_integer_subtype { type_name: $type_name, articled_display_name: $articled_display_name, temp_type_data: $value_type, + dyn_impls: {}, } impl ResolvableArgumentTarget for $type { diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index 740208a0..266574ab 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -7,6 +7,7 @@ define_leaf_type! { type_name: "untyped_int", articled_display_name: "an untyped integer", temp_type_data: UntypedIntegerTypeData, + dyn_impls: {}, } #[derive(Copy, Clone)] diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index 4de4fce0..74dd8580 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -1,5 +1,18 @@ use super::*; +pub(crate) trait IsIterable: 'static { + fn into_iterator(self: Box) -> ExecutionResult; + fn len(&self, error_span_range: SpanRange) -> ExecutionResult; +} + +define_dyn_type!( + pub(crate) IterableType, + content: dyn IsIterable, + dyn_kind: DynTypeKind::Iterable, + type_name: "iterable", + articled_display_name: "an iterable (e.g. array, list, etc.)", +); + // If you add a new variant, also update: // * ResolvableOwned for IterableValue // * IsArgument for IterableRef @@ -7,10 +20,10 @@ use super::*; pub(crate) enum IterableValue { Iterator(IteratorValue), Array(ArrayValue), - Stream(StreamValue), + Stream(OutputStream), Object(ObjectValue), Range(RangeValue), - String(StringValue), + String(String), } impl ResolvableArgumentTarget for IterableValue { @@ -22,10 +35,10 @@ impl ResolvableOwned for IterableValue { Ok(match value { Value::Array(x) => Self::Array(x), Value::Object(x) => Self::Object(x), - Value::Stream(x) => Self::Stream(x), + Value::Stream(x) => Self::Stream(x.value), Value::Range(x) => Self::Range(x), Value::Iterator(x) => Self::Iterator(x), - Value::String(x) => Self::String(x), + Value::String(x) => Self::String(x.value), _ => { return context.err( "an iterable (iterator, array, object, stream, range or string)", @@ -105,14 +118,14 @@ define_interface! { impl IterableValue { pub(crate) fn into_iterator(self) -> ExecutionResult { - Ok(match self { - IterableValue::Array(value) => IteratorValue::new_for_array(value), - IterableValue::Stream(value) => IteratorValue::new_for_stream(value), - IterableValue::Iterator(value) => value, - IterableValue::Range(value) => IteratorValue::new_for_range(value)?, - IterableValue::Object(value) => IteratorValue::new_for_object(value), - IterableValue::String(value) => IteratorValue::new_for_string(value), - }) + match self { + IterableValue::Array(value) => Box::new(value).into_iterator(), + IterableValue::Stream(value) => Box::new(value).into_iterator(), + IterableValue::Iterator(value) => Box::new(value).into_iterator(), + IterableValue::Range(value) => Box::new(value).into_iterator(), + IterableValue::Object(value) => Box::new(value).into_iterator(), + IterableValue::String(value) => Box::new(value).into_iterator(), + } } } @@ -122,7 +135,7 @@ pub(crate) enum IterableRef<'a> { Stream(AnyRef<'a, OutputStream>), Range(AnyRef<'a, RangeValue>), Object(AnyRef<'a, ObjectValue>), - String(AnyRef<'a, str>), + String(AnyRef<'a, String>), } impl IsArgument for IterableRef<'static> { @@ -151,14 +164,14 @@ impl IsArgument for IterableRef<'static> { impl Spanned> { pub(crate) fn len(&self) -> ExecutionResult { let Spanned(value, span) = self; + let span = *span; match value { - IterableRef::Iterator(iterator) => iterator.len(*span), - IterableRef::Array(value) => Ok(value.items.len()), - IterableRef::Stream(value) => Ok(value.len()), - IterableRef::Range(value) => value.len(*span), - IterableRef::Object(value) => Ok(value.entries.len()), - // NB - this is different to string.len() which counts bytes - IterableRef::String(value) => Ok(value.chars().count()), + IterableRef::Iterator(iterator) => iterator.len(span), + IterableRef::Array(value) => value.len(span), + IterableRef::Stream(value) => ::len(value, span), + IterableRef::Range(value) => value.len(span), + IterableRef::Object(value) => value.len(span), + IterableRef::String(value) => ::len(value, span), } } } diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index 57f97fd7..00127414 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -7,6 +7,17 @@ define_leaf_type! { type_name: "iterator", articled_display_name: "an iterator", temp_type_data: IteratorTypeData, + dyn_impls: { + impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(*self) + } + + fn len(&self, error_span_range: SpanRange) -> ExecutionResult { + self.len(error_span_range) + } + } + }, } #[derive(Clone)] @@ -28,10 +39,8 @@ impl IteratorValue { Self::new_vec(array.items.into_iter()) } - pub(crate) fn new_for_stream(stream: StreamValue) -> Self { - Self::new(IteratorValueInner::Stream(Box::new( - stream.value.into_iter(), - ))) + pub(crate) fn new_for_stream(stream: OutputStream) -> Self { + Self::new(IteratorValueInner::Stream(Box::new(stream.into_iter()))) } pub(crate) fn new_for_range(range: RangeValue) -> ExecutionResult { @@ -50,12 +59,13 @@ impl IteratorValue { Self::new_vec(iterator) } - pub(crate) fn new_for_string(string: StringValue) -> Self { + pub(crate) fn new_for_string_over_chars(string: String) -> Self { // We have to collect to vec and back to make the iterator owned // That's because value.chars() creates a `Chars<'_>` iterator which // borrows from the string, which we don't allow in a Boxed iterator + // TODO: Replace with the owned iterator here to avoid this clone: + // https://internals.rust-lang.org/t/is-there-a-good-reason-why-string-has-no-into-chars/19496/5 let iterator = string - .value .chars() .map(|c| c.into_value()) .collect::>() diff --git a/src/expressions/values/none.rs b/src/expressions/values/none.rs index e22b12cf..ad71fd5d 100644 --- a/src/expressions/values/none.rs +++ b/src/expressions/values/none.rs @@ -8,6 +8,7 @@ define_leaf_type! { // Instead of saying "expected a none value", we can say "expected None" articled_display_name: "None", temp_type_data: NoneTypeData, + dyn_impls: {}, } impl IntoValue for () { diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index 6c24183b..967cb515 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -7,6 +7,17 @@ define_leaf_type! { type_name: "object", articled_display_name: "an object", temp_type_data: ObjectTypeData, + dyn_impls: { + impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(IteratorValue::new_for_object(*self)) + } + + fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + Ok(self.entries.len()) + } + } + }, } #[derive(Clone)] diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 1ed29a2e..4c49dcc8 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -7,6 +7,7 @@ define_leaf_type! { type_name: "parser", articled_display_name: "a parser", temp_type_data: ParserTypeData, + dyn_impls: {}, } #[derive(Clone)] diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index aea075a3..c0f3ed92 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -9,6 +9,17 @@ define_leaf_type! { type_name: "range", articled_display_name: "a range", temp_type_data: RangeTypeData, + dyn_impls: { + impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + IteratorValue::new_for_range(*self) + } + + fn len(&self, error_span_range: SpanRange) -> ExecutionResult { + self.len(error_span_range) + } + } + }, } #[derive(Clone)] diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index 07e9e0b5..3d0d84ce 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -7,6 +7,17 @@ define_leaf_type! { type_name: "stream", articled_display_name: "a stream", temp_type_data: StreamTypeData, + dyn_impls: { + impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(IteratorValue::new_for_stream(*self)) + } + + fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + Ok(self.len()) + } + } + }, } #[derive(Clone)] diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index c7a13c90..9cd673c3 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -7,6 +7,19 @@ define_leaf_type! { type_name: "string", articled_display_name: "a string", temp_type_data: StringTypeData, + dyn_impls: { + impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(IteratorValue::new_for_string_over_chars(*self)) + } + + fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + // The iterator is over chars, so this must count chars. + // But contrast, string.len() counts bytes + Ok(self.chars().count()) + } + } + }, } #[derive(Clone)] diff --git a/src/expressions/values/unsupported_literal.rs b/src/expressions/values/unsupported_literal.rs index 11e3445d..7c85c7e9 100644 --- a/src/expressions/values/unsupported_literal.rs +++ b/src/expressions/values/unsupported_literal.rs @@ -7,6 +7,7 @@ define_leaf_type! { type_name: "unsupported_literal", articled_display_name: "an unsupported literal", temp_type_data: UnsupportedLiteralTypeData, + dyn_impls: {}, } #[derive(Clone)] From 95f88e02418ed529f3202f72a520e70d4df5f67e Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 4 Jan 2026 17:39:34 +0100 Subject: [PATCH 16/58] refactor: Rename MethodResolver to TypeFeatureResolver --- plans/TODO.md | 10 +++---- src/expressions/concepts/type_traits.rs | 12 ++++---- src/expressions/evaluation/value_frames.rs | 6 ++-- src/expressions/operations.rs | 8 ++++-- .../type_resolution/interface_macros.rs | 22 ++++++++++----- src/expressions/type_resolution/type_data.rs | 28 +++++++++++-------- src/expressions/type_resolution/type_kinds.rs | 22 +++++++-------- 7 files changed, 62 insertions(+), 46 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index f9278a2a..cafd89df 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -218,13 +218,15 @@ First, read the @./2025-11-vision.md - [x] They won't implement `IsArgumentForm` - [x] Create mappers and suitably generic `as_ref()` and `as_mut()` methods on `Actual` - [ ] Migrate method resolution to the trait macro properly - - [ ] Possibly separate own-type resolution `dyn TypeDetails` - - [ ] And property resolution `dyn TypeFeatureResolver` - - [ ] Allow dyns to be injected into resolution flow + - [x] And property resolution `dyn TypeFeatureResolver` + - [ ] Replace `impl TypeFeatureResolver for $type_def` + - [ ] Remove `temp_type_data: $type_data:ident,` + - [ ] Remove `HierarchicalTypeData` - [ ] Stage 1 of the form migration: - [ ] Replace `Owned` as type reference to `Actual<..>` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` - [ ] And similarly for other values... + - [ ] Strip wrapper types like `StreamValue` - can just use `OutputStream` as content - [ ] Stage 2 of the form migration: - [ ] Migrate `Shared`, `Mutable`, `Assignee` - [ ] Stage 3 @@ -234,8 +236,6 @@ First, read the @./2025-11-vision.md - [ ] CopyOnWrite, Shared => CopyOnWrite x2, Owned => CopyOnWrite - [ ] LateBound, Tons of conversions into it - [ ] Argument, and `ArgumentOwnership` driven conversions into it - - [ ] Strip wrapper types like `StreamValue` - can just use `OutputStream` as content - - [ ] Remove old definitions - [ ] Generate test over all value kinds which checks for: - [ ] Maybe over TypeKinds with https://docs.rs/inventory/latest/inventory/ registered as a dev dependency - [ ] Check for duplicate TypeKind registrations diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index b7d7f2da..6f7d9971 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -297,7 +297,7 @@ macro_rules! define_parent_type { } } - impl MethodResolver for $type_def { + impl TypeFeatureResolver for $type_def { fn resolve_method(&self, method_name: &str) -> Option { $type_data.resolve_method(method_name) } @@ -380,7 +380,7 @@ macro_rules! define_parent_type { $source_type_name } - pub(crate) fn method_resolver(&self) -> &'static dyn MethodResolver { + pub(crate) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { &$type_def } } @@ -412,9 +412,9 @@ macro_rules! define_parent_type { } } - fn method_resolver(&self) -> &'static dyn MethodResolver { + fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { match self { - $( Self::$variant(x) => x.method_resolver(), )* + $( Self::$variant(x) => x.feature_resolver(), )* } } } @@ -515,7 +515,7 @@ macro_rules! define_leaf_type { } } - impl MethodResolver for $type_def { + impl TypeFeatureResolver for $type_def { fn resolve_method(&self, method_name: &str) -> Option { $type_data.resolve_method(method_name) } @@ -558,7 +558,7 @@ macro_rules! define_leaf_type { $articled_display_name } - fn method_resolver(&self) -> &'static dyn MethodResolver { + fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { &$type_def } } diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index ba9c42f7..202a78ef 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -795,7 +795,7 @@ impl EvaluationFrame for UnaryOperationBuilder { // Try method resolution first if let Some(interface) = operand_kind - .method_resolver() + .feature_resolver() .resolve_unary_operation(&self.operation) { let operand_span = operand.span_range(); @@ -871,7 +871,7 @@ impl EvaluationFrame for BinaryOperationBuilder { // Try method resolution based on left operand's kind and resolve left operand immediately let interface = left .kind() - .method_resolver() + .feature_resolver() .resolve_binary_operation(&self.operation); match interface { @@ -1262,7 +1262,7 @@ impl EvaluationFrame for MethodCallBuilder { let caller = value.expect_late_bound(); let method = caller .kind() - .method_resolver() + .feature_resolver() .resolve_method(self.method.method.to_string().as_str()); let method = match method { Some(m) => m, diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index d36d4d77..21bfa5a6 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -89,7 +89,7 @@ impl UnaryOperation { let input = input.into_owned_value(); let method = input .kind() - .method_resolver() + .feature_resolver() .resolve_unary_operation(self) .ok_or_else(|| { self.type_error(format!( @@ -308,7 +308,11 @@ impl BinaryOperation { ) -> ExecutionResult> { let left = left.into_owned_value(); let right = right.into_owned_value(); - match left.kind().method_resolver().resolve_binary_operation(self) { + match left + .kind() + .feature_resolver() + .resolve_binary_operation(self) + { Some(interface) => { let left = interface .lhs_ownership diff --git a/src/expressions/type_resolution/interface_macros.rs b/src/expressions/type_resolution/interface_macros.rs index 39fc83b5..98f00672 100644 --- a/src/expressions/type_resolution/interface_macros.rs +++ b/src/expressions/type_resolution/interface_macros.rs @@ -13,6 +13,7 @@ macro_rules! if_empty { }; } +#[cfg(test)] macro_rules! handle_first_arg_type { ([NEXT] : $type:ty) => { $type @@ -382,29 +383,32 @@ macro_rules! define_interface { } #[allow(unused)] + #[cfg(test)] fn asserts() { + fn assert_first_argument>() {} + fn assert_output_type() {} $( - $type_data::assert_first_argument::(); + assert_first_argument::(); if_exists! { {$($method_ignore_type_assertion)?} {} - {$($type_data::assert_output_type::<$method_output_ty>();)?} + {$(assert_output_type::<$method_output_ty>();)?} } )* $( - $type_data::assert_first_argument::(); + assert_first_argument::(); if_exists! { {$($unary_ignore_type_assertion)?} {} - {$($type_data::assert_output_type::<$unary_output_ty>();)?} + {$(assert_output_type::<$unary_output_ty>();)?} } )* $( - $type_data::assert_first_argument::(); + assert_first_argument::(); if_exists! { {$($binary_ignore_type_assertion)?} {} - {$($type_data::assert_output_type::<$binary_output_ty>();)?} + {$(assert_output_type::<$binary_output_ty>();)?} } )* } @@ -472,7 +476,9 @@ macro_rules! define_interface { impl HierarchicalTypeData for $type_data { type Parent = $parent_type_data; const PARENT: Option = $mod_name::parent(); + } + impl TypeData for $type_data { #[allow(unreachable_code)] fn resolve_own_method(method_name: &str) -> Option { Some(match method_name { @@ -491,7 +497,9 @@ macro_rules! define_interface { } } +#[cfg(test)] +pub(crate) use handle_first_arg_type; pub(crate) use { define_interface, generate_binary_interface, generate_method_interface, - generate_unary_interface, handle_first_arg_type, if_empty, ignore_all, parse_arg_types, + generate_unary_interface, if_empty, ignore_all, parse_arg_types, }; diff --git a/src/expressions/type_resolution/type_data.rs b/src/expressions/type_resolution/type_data.rs index d508d138..4397a4f5 100644 --- a/src/expressions/type_resolution/type_data.rs +++ b/src/expressions/type_resolution/type_data.rs @@ -1,7 +1,7 @@ #![allow(clippy::type_complexity)] use super::*; -pub(crate) trait MethodResolver { +pub(crate) trait TypeFeatureResolver { /// Resolves a unary operation as a method interface for this type. fn resolve_method(&self, method_name: &str) -> Option; @@ -21,7 +21,7 @@ pub(crate) trait MethodResolver { fn resolve_type_property(&self, _property_name: &str) -> Option; } -impl MethodResolver for T { +impl TypeFeatureResolver for T { fn resolve_method(&self, method_name: &str) -> Option { match Self::resolve_own_method(method_name) { Some(method) => Some(method), @@ -50,36 +50,40 @@ impl MethodResolver for T { } fn resolve_type_property(&self, property_name: &str) -> Option { - ::resolve_type_property(property_name) + ::resolve_type_property(property_name) } } -pub(crate) trait HierarchicalTypeData { +// TODO[concepts]: Remove +pub(crate) trait HierarchicalTypeData: TypeData { type Parent: HierarchicalTypeData; const PARENT: Option; +} - fn assert_first_argument>() {} - - fn assert_output_type() {} - +pub(crate) trait TypeData { + /// Returns None if the method is not supported on this type itself. + /// The method may still be supported on a type further up the resolution chain. fn resolve_own_method(_method_name: &str) -> Option { None } - /// Resolves a unary operation as a method interface for this type. - /// Returns None if the operation should fallback to the legacy system. + /// Returns None if the operation is not supported on this type itself. + /// The operation may still be supported on a type further up the resolution chain. fn resolve_own_unary_operation(_operation: &UnaryOperation) -> Option { None } - /// Resolves a binary operation as a method interface for this type. - /// Returns None if the operation is not supported by this type. + /// Returns None if the operation is not supported on this type itself. + /// The operation may still be supported on a type further up the resolution chain. fn resolve_own_binary_operation( _operation: &BinaryOperation, ) -> Option { None } + /// Returns a property on the type. + /// Properties are *not* currently resolved up the resolution chain... but maybe they should be? + /// ... similarly, maybe functions should be too, when they are added? fn resolve_type_property(_property_name: &str) -> Option { None } diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs index a32e98be..1ffadd70 100644 --- a/src/expressions/type_resolution/type_kinds.rs +++ b/src/expressions/type_resolution/type_kinds.rs @@ -5,7 +5,7 @@ use super::*; pub(crate) trait IsSpecificLeafKind: Copy + Into { fn source_type_name(&self) -> &'static str; fn articled_display_name(&self) -> &'static str; - fn method_resolver(&self) -> &'static dyn MethodResolver; + fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver; } impl ValueLeafKind { @@ -82,11 +82,11 @@ impl ParentTypeKind { } } - pub(crate) fn method_resolver(&self) -> &'static dyn MethodResolver { + pub(crate) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { match self { - ParentTypeKind::Value(x) => x.method_resolver(), - ParentTypeKind::Integer(x) => x.method_resolver(), - ParentTypeKind::Float(x) => x.method_resolver(), + ParentTypeKind::Value(x) => x.feature_resolver(), + ParentTypeKind::Integer(x) => x.feature_resolver(), + ParentTypeKind::Float(x) => x.feature_resolver(), } } } @@ -109,7 +109,7 @@ impl DynTypeKind { } } - pub(crate) fn method_resolver(&self) -> &'static dyn MethodResolver { + pub(crate) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { match self { DynTypeKind::Iterable => &IterableTypeData, } @@ -166,11 +166,11 @@ impl ParseSource for TypeIdent { } impl TypeKind { - pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { + pub(in crate::expressions) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { match self { - TypeKind::Leaf(leaf_kind) => leaf_kind.method_resolver(), - TypeKind::Parent(parent_kind) => parent_kind.method_resolver(), - TypeKind::Dyn(dyn_kind) => dyn_kind.method_resolver(), + TypeKind::Leaf(leaf_kind) => leaf_kind.feature_resolver(), + TypeKind::Parent(parent_kind) => parent_kind.feature_resolver(), + TypeKind::Dyn(dyn_kind) => dyn_kind.feature_resolver(), } } } @@ -200,7 +200,7 @@ impl TypeProperty { &self, ownership: RequestedOwnership, ) -> ExecutionResult> { - let resolver = self.source_type.kind.method_resolver(); + let resolver = self.source_type.kind.feature_resolver(); // TODO[performance] - lazily initialize properties as Shared let resolved_property = resolver.resolve_type_property(&self.property.to_string()); match resolved_property { From f37f9a0df99aaf3f22e59b66831280edd9a3488c Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 5 Jan 2026 00:19:29 +0100 Subject: [PATCH 17/58] refactor: Migrate type feature resolution --- plans/TODO.md | 16 +-- src/expressions/concepts/type_traits.rs | 106 ++++++++------- src/expressions/type_resolution/arguments.rs | 8 +- .../type_resolution/interface_macros.rs | 31 +---- src/expressions/type_resolution/type_data.rs | 39 ------ src/expressions/type_resolution/type_kinds.rs | 2 +- src/expressions/values/array.rs | 11 +- src/expressions/values/boolean.rs | 9 +- src/expressions/values/character.rs | 9 +- src/expressions/values/float.rs | 9 +- src/expressions/values/float_subtypes.rs | 17 ++- src/expressions/values/float_untyped.rs | 11 +- src/expressions/values/integer.rs | 11 +- src/expressions/values/integer_subtypes.rs | 91 ++++--------- src/expressions/values/integer_untyped.rs | 11 +- src/expressions/values/iterable.rs | 12 +- src/expressions/values/iterator.rs | 11 +- src/expressions/values/none.rs | 9 +- src/expressions/values/object.rs | 11 +- src/expressions/values/parser.rs | 9 +- src/expressions/values/range.rs | 13 +- src/expressions/values/stream.rs | 11 +- src/expressions/values/string.rs | 13 +- src/expressions/values/unsupported_literal.rs | 7 +- src/expressions/values/value.rs | 7 +- src/misc/field_inputs.rs | 2 +- src/sandbox/dyn_value.rs | 124 ------------------ src/sandbox/mod.rs | 2 - 28 files changed, 195 insertions(+), 417 deletions(-) delete mode 100644 src/sandbox/dyn_value.rs delete mode 100644 src/sandbox/mod.rs diff --git a/plans/TODO.md b/plans/TODO.md index cafd89df..11980c28 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -217,11 +217,11 @@ First, read the @./2025-11-vision.md - [x] Create a `Ref` and a `Mut` form - [x] They won't implement `IsArgumentForm` - [x] Create mappers and suitably generic `as_ref()` and `as_mut()` methods on `Actual` - - [ ] Migrate method resolution to the trait macro properly + - [x] Migrate method resolution to the trait macro properly - [x] And property resolution `dyn TypeFeatureResolver` - - [ ] Replace `impl TypeFeatureResolver for $type_def` - - [ ] Remove `temp_type_data: $type_data:ident,` - - [ ] Remove `HierarchicalTypeData` + - [x] Replace `impl TypeFeatureResolver for $type_def` + - [x] Remove `temp_type_data: $type_data:ident,` + - [x] Remove `HierarchicalTypeData` - [ ] Stage 1 of the form migration: - [ ] Replace `Owned` as type reference to `Actual<..>` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` @@ -230,11 +230,11 @@ First, read the @./2025-11-vision.md - [ ] Stage 2 of the form migration: - [ ] Migrate `Shared`, `Mutable`, `Assignee` - [ ] Stage 3 - - [ ] Migrate `CopyOnWrite` .. + - [ ] Migrate `CopyOnWrite` and relevant interconversions - [ ] Finish migrating to new Argument/Returned resolution and delete old code + - [ ] Remove `impl_resolvable_argument_for` and `TODO[concepts]: Remove when we get rid of impl_resolvable_argument_for` - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): - - [ ] CopyOnWrite, Shared => CopyOnWrite x2, Owned => CopyOnWrite - - [ ] LateBound, Tons of conversions into it + - [ ] Consider migrating LateBound and interconversions - [ ] Argument, and `ArgumentOwnership` driven conversions into it - [ ] Generate test over all value kinds which checks for: - [ ] Maybe over TypeKinds with https://docs.rs/inventory/latest/inventory/ registered as a dev dependency @@ -244,7 +244,7 @@ First, read the @./2025-11-vision.md - [ ] Clear up all `TODO[concepts]` - [ ] Have variables store a `Referenceable` - [ ] Separate methods and functions - - [ ] Add ability to add comments to types, methods/functions and operations + - [ ] Add ability to add comments to types, methods/functions and operations, and generate docs from them ## Methods and closures diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 6f7d9971..6c771e89 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -121,6 +121,52 @@ pub(crate) trait IsChildType: IsHierarchicalType { ) -> Option>; } +macro_rules! impl_type_feature_resolver { + (impl TypeFeatureResolver for $type_def:ty: [$($type_defs:ty)+]) => { + impl TypeFeatureResolver for $type_def { + fn resolve_method(&self, method_name: &str) -> Option { + $( + if let Some(method) = <$type_defs as TypeData>::resolve_own_method(method_name) { + return Some(method); + }; + )+ + None + } + + fn resolve_unary_operation( + &self, + operation: &UnaryOperation, + ) -> Option { + $( + if let Some(operation) = <$type_defs as TypeData>::resolve_own_unary_operation(operation) { + return Some(operation); + }; + )+ + None + } + + fn resolve_binary_operation( + &self, + operation: &BinaryOperation, + ) -> Option { + $( + if let Some(operation) = <$type_defs as TypeData>::resolve_own_binary_operation(operation) { + return Some(operation); + }; + )+ + None + } + + fn resolve_type_property(&self, property_name: &str) -> Option { + // Purposefully doesn't resolve parents, but TBC if this is right + <$type_def as TypeData>::resolve_type_property(property_name) + } + } + }; +} + +pub(crate) use impl_type_feature_resolver; + macro_rules! impl_ancestor_chain_conversions { ($child:ty $(=> $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?) => { impl DowncastFrom<$child, F> for $child @@ -269,7 +315,6 @@ macro_rules! define_parent_type { }, type_name: $source_type_name:literal, articled_display_name: $articled_display_name:literal, - temp_type_data: $type_data:ident, ) => { $type_def_vis struct $type_def; @@ -297,28 +342,10 @@ macro_rules! define_parent_type { } } - impl TypeFeatureResolver for $type_def { - fn resolve_method(&self, method_name: &str) -> Option { - $type_data.resolve_method(method_name) - } - - fn resolve_unary_operation( - &self, - operation: &UnaryOperation, - ) -> Option { - $type_data.resolve_unary_operation(operation) - } - - fn resolve_binary_operation( - &self, - operation: &BinaryOperation, - ) -> Option { - $type_data.resolve_binary_operation(operation) - } - - fn resolve_type_property(&self, property_name: &str) -> Option { - $type_data.resolve_type_property(property_name) - } + impl_type_feature_resolver! { + impl TypeFeatureResolver for $type_def: [ + $type_def $( $parent $( $ancestor )* )? + ] } impl IsHierarchicalType for $type_def { @@ -450,9 +477,8 @@ macro_rules! define_leaf_type { kind: $kind_vis:vis $kind:ident, type_name: $source_type_name:literal, articled_display_name: $articled_display_name:literal, - temp_type_data: $type_data:ident, dyn_impls: { - $(impl $dyn_trait:ident { $($dyn_trait_impl:tt)* })* + $($dyn_type:ty: impl $dyn_trait:ident { $($dyn_trait_impl:tt)* })* }, ) => { $type_def_vis struct $type_def; @@ -515,28 +541,10 @@ macro_rules! define_leaf_type { } } - impl TypeFeatureResolver for $type_def { - fn resolve_method(&self, method_name: &str) -> Option { - $type_data.resolve_method(method_name) - } - - fn resolve_unary_operation( - &self, - operation: &UnaryOperation, - ) -> Option { - $type_data.resolve_unary_operation(operation) - } - - fn resolve_binary_operation( - &self, - operation: &BinaryOperation, - ) -> Option { - $type_data.resolve_binary_operation(operation) - } - - fn resolve_type_property(&self, property_name: &str) -> Option { - $type_data.resolve_type_property(property_name) - } + impl_type_feature_resolver! { + impl TypeFeatureResolver for $type_def: [ + $type_def $($dyn_type)* $parent $( $ancestor )* + ] } #[derive(Clone, Copy, PartialEq, Eq)] @@ -655,6 +663,10 @@ macro_rules! define_dyn_type { type DynContent = $dyn_type; } + impl_type_feature_resolver! { + impl TypeFeatureResolver for $type_def: [$type_def] + } + impl<'a> IsValueContent<'a> for $dyn_type { type Type = $type_def; type Form = BeOwned; diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index cc7ff338..7bc823c3 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -41,13 +41,13 @@ impl<'a> ResolutionContext<'a> { } pub(crate) trait IsArgument: Sized { - type ValueType: HierarchicalTypeData; + type ValueType: TypeData; const OWNERSHIP: ArgumentOwnership; fn from_argument(value: Spanned) -> ExecutionResult; } impl IsArgument for ArgumentValue { - type ValueType = ValueTypeData; + type ValueType = ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; fn from_argument(Spanned(value, _): Spanned) -> ExecutionResult { @@ -213,7 +213,7 @@ impl<'a, T: ResolvableMutable + ?Sized> ResolveAs> } pub(crate) trait ResolvableArgumentTarget { - type ValueType: HierarchicalTypeData; + type ValueType: TypeData; } pub(crate) trait ResolvableOwned: Sized { @@ -368,7 +368,7 @@ pub(crate) trait ResolvableMutable { } impl ResolvableArgumentTarget for Value { - type ValueType = ValueTypeData; + type ValueType = ValueType; } impl ResolvableOwned for Value { diff --git a/src/expressions/type_resolution/interface_macros.rs b/src/expressions/type_resolution/interface_macros.rs index 98f00672..3bbe2ae9 100644 --- a/src/expressions/type_resolution/interface_macros.rs +++ b/src/expressions/type_resolution/interface_macros.rs @@ -340,10 +340,10 @@ impl<'a> BinaryOperationCallContext<'a> { } } -macro_rules! define_interface { +macro_rules! define_type_features { ( - struct $type_data:ident, - parent: $parent_type_data:ident, + impl $type_def:ident, + parent: $parent_type_def:ident, $mod_vis:vis mod $mod_name:ident { $mod_methods_vis:vis mod methods { $( @@ -365,27 +365,13 @@ macro_rules! define_interface { } } ) => { - #[derive(Clone, Copy)] - pub(crate) struct $type_data; - $mod_vis mod $mod_name { use super::*; - $mod_vis const fn parent() -> Option<$parent_type_data> { - // Type ids aren't const, and strings aren't const-comparable, but I can use this work-around: - // https://internals.rust-lang.org/t/why-i-cannot-compare-two-static-str-s-in-a-const-context/17726/2 - const OWN_TYPE_NAME: &'static [u8] = stringify!($type_data).as_bytes(); - const PARENT_TYPE_NAME: &'static [u8] = stringify!($parent_type_data).as_bytes(); - match PARENT_TYPE_NAME { - OWN_TYPE_NAME => None, - _ => Some($parent_type_data), - } - } - #[allow(unused)] #[cfg(test)] fn asserts() { - fn assert_first_argument>() {} + fn assert_first_argument>() {} fn assert_output_type() {} $( assert_first_argument::(); @@ -473,12 +459,7 @@ macro_rules! define_interface { )* } - impl HierarchicalTypeData for $type_data { - type Parent = $parent_type_data; - const PARENT: Option = $mod_name::parent(); - } - - impl TypeData for $type_data { + impl TypeData for $type_def { #[allow(unreachable_code)] fn resolve_own_method(method_name: &str) -> Option { Some(match method_name { @@ -500,6 +481,6 @@ macro_rules! define_interface { #[cfg(test)] pub(crate) use handle_first_arg_type; pub(crate) use { - define_interface, generate_binary_interface, generate_method_interface, + define_type_features, generate_binary_interface, generate_method_interface, generate_unary_interface, if_empty, ignore_all, parse_arg_types, }; diff --git a/src/expressions/type_resolution/type_data.rs b/src/expressions/type_resolution/type_data.rs index 4397a4f5..ae7d80ff 100644 --- a/src/expressions/type_resolution/type_data.rs +++ b/src/expressions/type_resolution/type_data.rs @@ -21,45 +21,6 @@ pub(crate) trait TypeFeatureResolver { fn resolve_type_property(&self, _property_name: &str) -> Option; } -impl TypeFeatureResolver for T { - fn resolve_method(&self, method_name: &str) -> Option { - match Self::resolve_own_method(method_name) { - Some(method) => Some(method), - None => Self::PARENT.and_then(|p| p.resolve_method(method_name)), - } - } - - fn resolve_unary_operation( - &self, - operation: &UnaryOperation, - ) -> Option { - match Self::resolve_own_unary_operation(operation) { - Some(method) => Some(method), - None => Self::PARENT.and_then(|p| p.resolve_unary_operation(operation)), - } - } - - fn resolve_binary_operation( - &self, - operation: &BinaryOperation, - ) -> Option { - match Self::resolve_own_binary_operation(operation) { - Some(method) => Some(method), - None => Self::PARENT.and_then(|p| p.resolve_binary_operation(operation)), - } - } - - fn resolve_type_property(&self, property_name: &str) -> Option { - ::resolve_type_property(property_name) - } -} - -// TODO[concepts]: Remove -pub(crate) trait HierarchicalTypeData: TypeData { - type Parent: HierarchicalTypeData; - const PARENT: Option; -} - pub(crate) trait TypeData { /// Returns None if the method is not supported on this type itself. /// The method may still be supported on a type further up the resolution chain. diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs index 1ffadd70..ef7ca61a 100644 --- a/src/expressions/type_resolution/type_kinds.rs +++ b/src/expressions/type_resolution/type_kinds.rs @@ -111,7 +111,7 @@ impl DynTypeKind { pub(crate) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { match self { - DynTypeKind::Iterable => &IterableTypeData, + DynTypeKind::Iterable => &IterableType, } } } diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 25a86401..4754dac3 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -6,9 +6,8 @@ define_leaf_type! { kind: pub(crate) ArrayKind, type_name: "array", articled_display_name: "an array", - temp_type_data: ArrayTypeData, dyn_impls: { - impl IsIterable { + IterableType: impl IsIterable { fn into_iterator(self: Box) -> ExecutionResult { Ok(IteratorValue::new_for_array(*self)) } @@ -183,7 +182,7 @@ impl IntoValue for ArrayValue { } impl_resolvable_argument_for! { - ArrayTypeData, + ArrayType, (value, context) -> ArrayValue { match value { Value::Array(value) => Ok(value), @@ -192,9 +191,9 @@ impl_resolvable_argument_for! { } } -define_interface! { - struct ArrayTypeData, - parent: IterableTypeData, +define_type_features! { + impl ArrayType, + parent: IterableType, pub(crate) mod array_interface { pub(crate) mod methods { fn push(mut this: Mutable, item: OwnedValue) -> ExecutionResult<()> { diff --git a/src/expressions/values/boolean.rs b/src/expressions/values/boolean.rs index 1646ae1c..2fad22d1 100644 --- a/src/expressions/values/boolean.rs +++ b/src/expressions/values/boolean.rs @@ -8,7 +8,6 @@ define_leaf_type! { kind: pub(crate) BoolKind, type_name: "bool", articled_display_name: "a bool", - temp_type_data: BooleanTypeData, dyn_impls: {}, } @@ -63,9 +62,9 @@ impl IntoValue for bool { } } -define_interface! { - struct BooleanTypeData, - parent: ValueTypeData, +define_type_features! { + impl BoolType, + parent: ValueType, pub(crate) mod boolean_interface { pub(crate) mod methods { } @@ -227,7 +226,7 @@ define_interface! { } impl_resolvable_argument_for! { - BooleanTypeData, + BoolType, (value, context) -> BooleanValue { match value { Value::Boolean(value) => Ok(value), diff --git a/src/expressions/values/character.rs b/src/expressions/values/character.rs index 3e447b25..16d0364c 100644 --- a/src/expressions/values/character.rs +++ b/src/expressions/values/character.rs @@ -6,7 +6,6 @@ define_leaf_type! { kind: pub(crate) CharKind, type_name: "char", articled_display_name: "a char", - temp_type_data: CharTypeData, dyn_impls: {}, } @@ -61,9 +60,9 @@ impl IntoValue for char { } } -define_interface! { - struct CharTypeData, - parent: ValueTypeData, +define_type_features! { + impl CharType, + parent: ValueType, pub(crate) mod char_interface { pub(crate) mod methods { } @@ -196,7 +195,7 @@ define_interface! { } impl_resolvable_argument_for! { - CharTypeData, + CharType, (value, context) -> CharValue { match value { Value::Char(value) => Ok(value), diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index 826f4e81..d1ddf7b6 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -12,7 +12,6 @@ define_parent_type! { }, type_name: "float", articled_display_name: "a float", - temp_type_data: FloatTypeData, } #[derive(Copy, Clone)] @@ -189,9 +188,9 @@ impl ValuesEqual for FloatValue { } } -define_interface! { - struct FloatTypeData, - parent: ValueTypeData, +define_type_features! { + impl FloatType, + parent: ValueType, pub(crate) mod float_interface { pub(crate) mod methods { fn is_nan(this: FloatValue) -> bool { @@ -377,7 +376,7 @@ define_interface! { } impl_resolvable_argument_for! { - FloatTypeData, + FloatType, (value, context) -> FloatValue { match value { Value::Float(value) => Ok(value), diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index f66193cd..769d2ceb 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -4,9 +4,9 @@ macro_rules! impl_float_operations { ( $($type_data:ident mod $mod_name:ident: $float_enum_variant:ident($float_type:ident)),* $(,)? ) => {$( - define_interface! { - struct $type_data, - parent: FloatTypeData, + define_type_features! { + impl $type_data, + parent: FloatType, pub(crate) mod $mod_name { pub(crate) mod methods { } @@ -153,22 +153,21 @@ macro_rules! impl_float_operations { )*}; } -impl_float_operations!(F32TypeData mod f32_interface: F32(f32), F64TypeData mod f64_interface: F64(f64)); +impl_float_operations!(F32Type mod f32_interface: F32(f32), F64Type mod f64_interface: F64(f64)); macro_rules! impl_resolvable_float_subtype { - ($type_def:ident, $kind:ident, $value_type:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { + ($type_def:ident, $kind:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { define_leaf_type! { pub(crate) $type_def => FloatType(FloatContent::$variant) => ValueType, content: $type, kind: pub(crate) $kind, type_name: $type_name, articled_display_name: $articled_display_name, - temp_type_data: $value_type, dyn_impls: {}, } impl ResolvableArgumentTarget for $type { - type ValueType = $value_type; + type ValueType = $type_def; } impl From<$type> for FloatValue { @@ -228,5 +227,5 @@ macro_rules! impl_resolvable_float_subtype { }; } -impl_resolvable_float_subtype!(F32Type, F32Kind, F32TypeData, f32, F32, "f32", "an f32"); -impl_resolvable_float_subtype!(F64Type, F64Kind, F64TypeData, f64, F64, "f64", "an f64"); +impl_resolvable_float_subtype!(F32Type, F32Kind, f32, F32, "f32", "an f32"); +impl_resolvable_float_subtype!(F64Type, F64Kind, f64, F64, "f64", "an f64"); diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 2368f0a5..10828bb0 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -6,7 +6,6 @@ define_leaf_type! { kind: pub(crate) UntypedFloatKind, type_name: "untyped_float", articled_display_name: "an untyped float", - temp_type_data: UntypedFloatTypeData, dyn_impls: {}, } @@ -77,9 +76,9 @@ impl IntoValue for UntypedFloat { } } -define_interface! { - struct UntypedFloatTypeData, - parent: FloatTypeData, +define_type_features! { + impl UntypedFloatType, + parent: FloatType, pub(crate) mod untyped_float_interface { pub(crate) mod methods { } @@ -199,7 +198,7 @@ define_interface! { pub(crate) struct UntypedFloatFallback(pub FallbackFloat); impl ResolvableArgumentTarget for UntypedFloatFallback { - type ValueType = UntypedFloatTypeData; + type ValueType = UntypedFloatType; } impl ResolvableOwned for UntypedFloatFallback { @@ -219,7 +218,7 @@ impl ResolvableOwned for UntypedFloat { } impl_resolvable_argument_for! { - UntypedFloatTypeData, + UntypedFloatType, (value, context) -> UntypedFloat { match value { Value::Float(FloatValue::Untyped(x)) => Ok(x), diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index cf0c3b2d..db16ea89 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -22,7 +22,6 @@ define_parent_type! { }, type_name: "int", articled_display_name: "an integer", - temp_type_data: IntegerTypeData, } #[derive(Copy, Clone)] @@ -245,9 +244,9 @@ impl ValuesEqual for IntegerValue { } } -define_interface! { - struct IntegerTypeData, - parent: ValueTypeData, +define_type_features! { + impl IntegerType, + parent: ValueType, pub(crate) mod integer_interface { pub(crate) mod methods { } @@ -625,7 +624,7 @@ define_interface! { } impl_resolvable_argument_for! { - IntegerTypeData, + IntegerType, (value, context) -> IntegerValue { match value { Value::Integer(value) => Ok(value), @@ -637,7 +636,7 @@ impl_resolvable_argument_for! { pub(crate) struct CoercedToU32(pub(crate) u32); impl ResolvableArgumentTarget for CoercedToU32 { - type ValueType = IntegerTypeData; + type ValueType = IntegerType; } impl ResolvableOwned for CoercedToU32 { diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 4e72b373..53c5a09c 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -5,9 +5,9 @@ macro_rules! impl_int_operations { ( $($integer_type_data:ident mod $mod_name:ident: [$(CharCast[$char_cast:ident],)?$(Signed[$signed:ident],)?] $integer_enum_variant:ident($integer_type:ident)),* $(,)? ) => {$( - define_interface! { - struct $integer_type_data, - parent: IntegerTypeData, + define_type_features! { + impl $integer_type_data, + parent: IntegerType, pub(crate) mod $mod_name { pub(crate) mod methods { } @@ -174,34 +174,33 @@ macro_rules! impl_int_operations { } impl_int_operations!( - U8TypeData mod u8_interface: [CharCast[yes],] U8(u8), - U16TypeData mod u16_interface: [] U16(u16), - U32TypeData mod u32_interface: [] U32(u32), - U64TypeData mod u64_interface: [] U64(u64), - U128TypeData mod u128_interface: [] U128(u128), - UsizeTypeData mod usize_interface: [] Usize(usize), - I8TypeData mod i8_interface: [Signed[yes],] I8(i8), - I16TypeData mod i16_interface: [Signed[yes],] I16(i16), - I32TypeData mod i32_interface: [Signed[yes],] I32(i32), - I64TypeData mod i64_interface: [Signed[yes],] I64(i64), - I128TypeData mod i128_interface: [Signed[yes],] I128(i128), - IsizeTypeData mod isize_interface: [Signed[yes],] Isize(isize), + U8Type mod u8_interface: [CharCast[yes],] U8(u8), + U16Type mod u16_interface: [] U16(u16), + U32Type mod u32_interface: [] U32(u32), + U64Type mod u64_interface: [] U64(u64), + U128Type mod u128_interface: [] U128(u128), + UsizeType mod usize_interface: [] Usize(usize), + I8Type mod i8_interface: [Signed[yes],] I8(i8), + I16Type mod i16_interface: [Signed[yes],] I16(i16), + I32Type mod i32_interface: [Signed[yes],] I32(i32), + I64Type mod i64_interface: [Signed[yes],] I64(i64), + I128Type mod i128_interface: [Signed[yes],] I128(i128), + IsizeType mod isize_interface: [Signed[yes],] Isize(isize), ); macro_rules! impl_resolvable_integer_subtype { - ($type_def:ident, $kind:ident, $value_type:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { + ($type_def:ident, $kind:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { define_leaf_type! { pub(crate) $type_def => IntegerType(IntegerContent::$variant) => ValueType, content: $type, kind: pub(crate) $kind, type_name: $type_name, articled_display_name: $articled_display_name, - temp_type_data: $value_type, dyn_impls: {}, } impl ResolvableArgumentTarget for $type { - type ValueType = $value_type; + type ValueType = $type_def; } impl From<$type> for IntegerValue { @@ -261,47 +260,15 @@ macro_rules! impl_resolvable_integer_subtype { }; } -impl_resolvable_integer_subtype!(I8Type, I8Kind, I8TypeData, i8, I8, "i8", "an i8"); -impl_resolvable_integer_subtype!(I16Type, I16Kind, I16TypeData, i16, I16, "i16", "an i16"); -impl_resolvable_integer_subtype!(I32Type, I32Kind, I32TypeData, i32, I32, "i32", "an i32"); -impl_resolvable_integer_subtype!(I64Type, I64Kind, I64TypeData, i64, I64, "i64", "an i64"); -impl_resolvable_integer_subtype!( - I128Type, - I128Kind, - I128TypeData, - i128, - I128, - "i128", - "an i128" -); -impl_resolvable_integer_subtype!( - IsizeType, - IsizeKind, - IsizeTypeData, - isize, - Isize, - "isize", - "an isize" -); -impl_resolvable_integer_subtype!(U8Type, U8Kind, U8TypeData, u8, U8, "u8", "a u8"); -impl_resolvable_integer_subtype!(U16Type, U16Kind, U16TypeData, u16, U16, "u16", "a u16"); -impl_resolvable_integer_subtype!(U32Type, U32Kind, U32TypeData, u32, U32, "u32", "a u32"); -impl_resolvable_integer_subtype!(U64Type, U64Kind, U64TypeData, u64, U64, "u64", "a u64"); -impl_resolvable_integer_subtype!( - U128Type, - U128Kind, - U128TypeData, - u128, - U128, - "u128", - "a u128" -); -impl_resolvable_integer_subtype!( - UsizeType, - UsizeKind, - UsizeTypeData, - usize, - Usize, - "usize", - "a usize" -); +impl_resolvable_integer_subtype!(I8Type, I8Kind, i8, I8, "i8", "an i8"); +impl_resolvable_integer_subtype!(I16Type, I16Kind, i16, I16, "i16", "an i16"); +impl_resolvable_integer_subtype!(I32Type, I32Kind, i32, I32, "i32", "an i32"); +impl_resolvable_integer_subtype!(I64Type, I64Kind, i64, I64, "i64", "an i64"); +impl_resolvable_integer_subtype!(I128Type, I128Kind, i128, I128, "i128", "an i128"); +impl_resolvable_integer_subtype!(IsizeType, IsizeKind, isize, Isize, "isize", "an isize"); +impl_resolvable_integer_subtype!(U8Type, U8Kind, u8, U8, "u8", "a u8"); +impl_resolvable_integer_subtype!(U16Type, U16Kind, u16, U16, "u16", "a u16"); +impl_resolvable_integer_subtype!(U32Type, U32Kind, u32, U32, "u32", "a u32"); +impl_resolvable_integer_subtype!(U64Type, U64Kind, u64, U64, "u64", "a u64"); +impl_resolvable_integer_subtype!(U128Type, U128Kind, u128, U128, "u128", "a u128"); +impl_resolvable_integer_subtype!(UsizeType, UsizeKind, usize, Usize, "usize", "a usize"); diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index 266574ab..d20b2cf4 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -6,7 +6,6 @@ define_leaf_type! { kind: pub(crate) UntypedIntegerKind, type_name: "untyped_int", articled_display_name: "an untyped integer", - temp_type_data: UntypedIntegerTypeData, dyn_impls: {}, } @@ -128,9 +127,9 @@ impl IntoValue for UntypedInteger { } } -define_interface! { - struct UntypedIntegerTypeData, - parent: IntegerTypeData, +define_type_features! { + impl UntypedIntegerType, + parent: IntegerType, pub(crate) mod untyped_integer_interface { pub(crate) mod methods { } @@ -255,7 +254,7 @@ define_interface! { pub(crate) struct UntypedIntegerFallback(pub(crate) FallbackInteger); impl ResolvableArgumentTarget for UntypedIntegerFallback { - type ValueType = UntypedIntegerTypeData; + type ValueType = UntypedIntegerType; } impl ResolvableOwned for UntypedIntegerFallback { @@ -279,7 +278,7 @@ impl ResolvableOwned for UntypedInteger { } impl_resolvable_argument_for! { - UntypedIntegerTypeData, + UntypedIntegerType, (value, context) -> UntypedInteger { match value { Value::Integer(IntegerValue::Untyped(x)) => Ok(x), diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index 74dd8580..8f812c62 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -16,7 +16,7 @@ define_dyn_type!( // If you add a new variant, also update: // * ResolvableOwned for IterableValue // * IsArgument for IterableRef -// * The parent of the value's TypeData to be IterableTypeData +// * The parent of the value's TypeData to be IterableType pub(crate) enum IterableValue { Iterator(IteratorValue), Array(ArrayValue), @@ -27,7 +27,7 @@ pub(crate) enum IterableValue { } impl ResolvableArgumentTarget for IterableValue { - type ValueType = IterableTypeData; + type ValueType = IterableType; } impl ResolvableOwned for IterableValue { @@ -49,9 +49,9 @@ impl ResolvableOwned for IterableValue { } } -define_interface! { - struct IterableTypeData, - parent: ValueTypeData, +define_type_features! { + impl IterableType, + parent: ValueType, pub(crate) mod iterable_interface { pub(crate) mod methods { fn into_iter(this: IterableValue) -> ExecutionResult { @@ -139,7 +139,7 @@ pub(crate) enum IterableRef<'a> { } impl IsArgument for IterableRef<'static> { - type ValueType = IterableTypeData; + type ValueType = IterableType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; fn from_argument(argument: Spanned) -> ExecutionResult { diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index 00127414..99057912 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -6,9 +6,8 @@ define_leaf_type! { kind: pub(crate) IteratorKind, type_name: "iterator", articled_display_name: "an iterator", - temp_type_data: IteratorTypeData, dyn_impls: { - impl IsIterable { + IterableType: impl IsIterable { fn into_iterator(self: Box) -> ExecutionResult { Ok(*self) } @@ -205,7 +204,7 @@ impl IntoValue for IteratorValue { } impl_resolvable_argument_for! { - IteratorTypeData, + IteratorType, (value, context) -> IteratorValue { match value { Value::Iterator(value) => Ok(value), @@ -299,9 +298,9 @@ impl Iterator for Mutable { } } -define_interface! { - struct IteratorTypeData, - parent: IterableTypeData, +define_type_features! { + impl IteratorType, + parent: IterableType, pub(crate) mod iterator_interface { pub(crate) mod methods { fn next(mut this: Mutable) -> Value { diff --git a/src/expressions/values/none.rs b/src/expressions/values/none.rs index ad71fd5d..47ad2d66 100644 --- a/src/expressions/values/none.rs +++ b/src/expressions/values/none.rs @@ -7,7 +7,6 @@ define_leaf_type! { type_name: "none", // Instead of saying "expected a none value", we can say "expected None" articled_display_name: "None", - temp_type_data: NoneTypeData, dyn_impls: {}, } @@ -18,7 +17,7 @@ impl IntoValue for () { } impl ResolvableArgumentTarget for () { - type ValueType = NoneTypeData; + type ValueType = NoneType; } impl ResolvableOwned for () { @@ -36,9 +35,9 @@ define_optional_object! { } } -define_interface! { - struct NoneTypeData, - parent: ValueTypeData, +define_type_features! { + impl NoneType, + parent: ValueType, pub(crate) mod none_interface { pub(crate) mod methods { [context] fn configure_preinterpret(_none: (), inputs: SettingsInputs) { diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index 967cb515..9cb4afdd 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -6,9 +6,8 @@ define_leaf_type! { kind: pub(crate) ObjectKind, type_name: "object", articled_display_name: "an object", - temp_type_data: ObjectTypeData, dyn_impls: { - impl IsIterable { + IterableType: impl IsIterable { fn into_iterator(self: Box) -> ExecutionResult { Ok(IteratorValue::new_for_object(*self)) } @@ -32,7 +31,7 @@ impl IntoValue for ObjectValue { } impl_resolvable_argument_for! { - ObjectTypeData, + ObjectType, (value, context) -> ObjectValue { match value { Value::Object(value) => Ok(value), @@ -267,9 +266,9 @@ impl IntoValue for BTreeMap { } } -define_interface! { - struct ObjectTypeData, - parent: IterableTypeData, +define_type_features! { + impl ObjectType, + parent: IterableType, pub(crate) mod object_interface { pub(crate) mod methods { [context] fn zip(this: ObjectValue) -> ExecutionResult { diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 4c49dcc8..a57c0799 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -6,7 +6,6 @@ define_leaf_type! { kind: pub(crate) ParserKind, type_name: "parser", articled_display_name: "a parser", - temp_type_data: ParserTypeData, dyn_impls: {}, } @@ -100,9 +99,9 @@ fn parser<'a>( this.parser(context.interpreter) } -define_interface! { - struct ParserTypeData, - parent: ValueTypeData, +define_type_features! { + impl ParserType, + parent: ValueType, pub(crate) mod parser_interface { pub(crate) mod methods { // GENERAL @@ -289,7 +288,7 @@ define_interface! { } impl_resolvable_argument_for! { - ParserTypeData, + ParserType, (value, context) -> ParserValue { match value { Value::Parser(value) => Ok(value), diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index c0f3ed92..55b27fb8 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -8,9 +8,8 @@ define_leaf_type! { kind: pub(crate) RangeKind, type_name: "range", articled_display_name: "a range", - temp_type_data: RangeTypeData, dyn_impls: { - impl IsIterable { + IterableType: impl IsIterable { fn into_iterator(self: Box) -> ExecutionResult { IteratorValue::new_for_range(*self) } @@ -351,7 +350,7 @@ impl IntoValue for RangeValueInner { } impl_resolvable_argument_for! { - RangeTypeData, + RangeType, (value, context) -> RangeValue { match value { Value::Range(value) => Ok(value), @@ -360,9 +359,9 @@ impl_resolvable_argument_for! { } } -define_interface! { - struct RangeTypeData, - parent: IterableTypeData, +define_type_features! { + impl RangeType, + parent: IterableType, pub(crate) mod range_interface { pub(crate) mod methods { } @@ -377,7 +376,7 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Cast { .. } - if IteratorTypeData::resolve_own_unary_operation(operation).is_some() => + if IteratorType::resolve_own_unary_operation(operation).is_some() => { unary_definitions::cast_via_iterator() } diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index 3d0d84ce..96e66c7b 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -6,9 +6,8 @@ define_leaf_type! { kind: pub(crate) StreamKind, type_name: "stream", articled_display_name: "a stream", - temp_type_data: StreamTypeData, dyn_impls: { - impl IsIterable { + IterableType: impl IsIterable { fn into_iterator(self: Box) -> ExecutionResult { Ok(IteratorValue::new_for_stream(*self)) } @@ -134,7 +133,7 @@ impl IntoValue for TokenStream { } impl_resolvable_argument_for! { - StreamTypeData, + StreamType, (value, context) -> StreamValue { match value { Value::Stream(value) => Ok(value), @@ -147,9 +146,9 @@ impl_delegated_resolvable_argument_for!( (value: StreamValue) -> OutputStream { value.value } ); -define_interface! { - struct StreamTypeData, - parent: IterableTypeData, +define_type_features! { + impl StreamType, + parent: IterableType, pub(crate) mod stream_interface { pub(crate) mod methods { // This is also on iterable, but is specialized here for performance diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index 9cd673c3..464b161b 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -6,9 +6,8 @@ define_leaf_type! { kind: pub(crate) StringKind, type_name: "string", articled_display_name: "a string", - temp_type_data: StringTypeData, dyn_impls: { - impl IsIterable { + IterableType: impl IsIterable { fn into_iterator(self: Box) -> ExecutionResult { Ok(IteratorValue::new_for_string_over_chars(*self)) } @@ -103,9 +102,9 @@ pub(crate) fn string_to_literal( Ok(literal.with_span(span)) } -define_interface! { - struct StringTypeData, - parent: IterableTypeData, +define_type_features! { + impl StringType, + parent: IterableType, pub(crate) mod string_interface { pub(crate) mod methods { // ================== @@ -251,7 +250,7 @@ define_interface! { } impl_resolvable_argument_for! { - StringTypeData, + StringType, (value, context) -> StringValue { match value { Value::String(value) => Ok(value), @@ -265,7 +264,7 @@ impl_delegated_resolvable_argument_for!( ); impl ResolvableArgumentTarget for str { - type ValueType = StringTypeData; + type ValueType = StringType; } impl ResolvableShared for str { diff --git a/src/expressions/values/unsupported_literal.rs b/src/expressions/values/unsupported_literal.rs index 7c85c7e9..c3f779cd 100644 --- a/src/expressions/values/unsupported_literal.rs +++ b/src/expressions/values/unsupported_literal.rs @@ -6,7 +6,6 @@ define_leaf_type! { kind: pub(crate) UnsupportedLiteralKind, type_name: "unsupported_literal", articled_display_name: "an unsupported literal", - temp_type_data: UnsupportedLiteralTypeData, dyn_impls: {}, } @@ -39,9 +38,9 @@ impl HasLeafKind for UnsupportedLiteral { } } -define_interface! { - struct UnsupportedLiteralTypeData, - parent: ValueTypeData, +define_type_features! { + impl UnsupportedLiteralType, + parent: ValueType, pub(crate) mod unsupported_literal_interface { pub(crate) mod methods {} pub(crate) mod unary_operations {} diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index 1d677ad4..a36f7c14 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -30,7 +30,6 @@ define_parent_type! { }, type_name: "value", articled_display_name: "any value", - temp_type_data: ValueTypeData, } #[derive(Clone)] @@ -52,9 +51,9 @@ pub(crate) enum Value { Parser(ParserValue), } -define_interface! { - struct ValueTypeData, - parent: ValueTypeData, +define_type_features! { + impl ValueType, + parent: ValueType, pub(crate) mod value_interface { pub(crate) mod methods { fn clone(this: CopyOnWriteValue) -> OwnedValue { diff --git a/src/misc/field_inputs.rs b/src/misc/field_inputs.rs index aaf34ed3..987d66f7 100644 --- a/src/misc/field_inputs.rs +++ b/src/misc/field_inputs.rs @@ -64,7 +64,7 @@ macro_rules! define_typed_object { } impl ResolvableArgumentTarget for $model { - type ValueType = ObjectTypeData; + type ValueType = ObjectType; } impl ResolvableOwned for $model { diff --git a/src/sandbox/dyn_value.rs b/src/sandbox/dyn_value.rs deleted file mode 100644 index 91a6645f..00000000 --- a/src/sandbox/dyn_value.rs +++ /dev/null @@ -1,124 +0,0 @@ -#![allow(unused)] - -// NOTE: This was an alternative design for modelling the type structure -// But we ended up going with enums/GATs instead for simplicity -// Non-hierarchical types such as "Iterable" are defined as `Box` though - -use std::any::Any; -trait IsValue: Any { - type TypeData: 'static + TypeData; -} -trait DynValue: Any { - fn type_data(&self) -> &dyn DynTypeData; -} -impl DynValue for T { - fn type_data(&self) -> &'static dyn DynTypeData { - ::TypeData::static_ref() - } -} - -trait IsIterable { - fn do_something(self) -> String; -} - -trait DynIterable { - fn do_something(self: Box) -> String; -} - -impl DynIterable for T { - fn do_something(self: Box) -> String { - ::do_something(*self) - } -} - -enum IntegerValue { - U32(u32), - // Other integer types could be added here -} - -impl IsValue for u32 { - type TypeData = U32TypeData; -} -struct AnyRef<'a, T: ?Sized + 'static> { - value: &'a T, -} - -trait TypeData { - type Value: 'static + IsValue; - - fn static_ref() -> &'static dyn DynTypeData; - - fn as_value(value: Box) -> Option { - let value_any: Box = value; - value_any.downcast::().ok().map(|v| *v) - } - - fn as_value_ref(value: AnyRef) -> Option> { - let value_any: &dyn Any = value.value; - value_any - .downcast_ref::() - .map(|v| AnyRef { value: v }) - } - - fn as_integer(value: Self::Value) -> Option { - let _ = value; - None - } - - fn as_iterable(value: Self::Value) -> Option> { - let _ = value; - None - } -} - -trait DynTypeData { - fn as_integer(&self, value: Box) -> Option; - fn as_iterable(&self, value: Box) -> Option>; -} - -impl DynTypeData for T { - fn as_integer(&self, value: Box) -> Option { - T::as_integer(Self::as_value(value)?) - } - - fn as_iterable(&self, value: Box) -> Option> { - T::as_iterable(Self::as_value(value)?) - } -} - -struct U32TypeData; - -impl TypeData for U32TypeData { - type Value = u32; - - fn static_ref() -> &'static dyn DynTypeData { - &Self - } - - fn as_integer(value: Self::Value) -> Option { - Some(IntegerValue::U32(value)) - } -} - -struct ArrayValue; -impl IsValue for ArrayValue { - type TypeData = ArrayTypeData; -} -impl IsIterable for ArrayValue { - fn do_something(self) -> String { - "ArrayValue iterable".to_string() - } -} -struct ArrayTypeData; - -impl TypeData for ArrayTypeData { - type Value = ArrayValue; - - fn static_ref() -> &'static dyn DynTypeData { - &Self - } - - fn as_iterable(value: Self::Value) -> Option> { - Some(Box::new(value)) - } -} diff --git a/src/sandbox/mod.rs b/src/sandbox/mod.rs deleted file mode 100644 index 50e1436c..00000000 --- a/src/sandbox/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod dyn_value; -mod gat_value; From 58e4a1999474ca2b15ee703940a00cdc0a0eeb7c Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 5 Jan 2026 02:34:56 +0100 Subject: [PATCH 18/58] refactor: Migrate parent type content --- plans/TODO.md | 19 +- src/expressions/concepts/actual.rs | 22 ++ src/expressions/concepts/form.rs | 6 +- src/expressions/concepts/forms/any_mut.rs | 1 + src/expressions/concepts/forms/any_ref.rs | 1 + src/expressions/concepts/forms/argument.rs | 1 + src/expressions/concepts/forms/assignee.rs | 1 + .../concepts/forms/copy_on_write.rs | 1 + src/expressions/concepts/forms/late_bound.rs | 1 + src/expressions/concepts/forms/mutable.rs | 1 + src/expressions/concepts/forms/owned.rs | 10 + .../concepts/forms/referenceable.rs | 1 + src/expressions/concepts/forms/shared.rs | 1 + src/expressions/concepts/forms/simple_mut.rs | 1 + src/expressions/concepts/forms/simple_ref.rs | 1 + src/expressions/concepts/type_traits.rs | 95 +++++++- src/expressions/control_flow.rs | 2 +- .../evaluation/assignment_frames.rs | 2 +- src/expressions/expression_block.rs | 2 +- src/expressions/expression_parsing.rs | 4 +- src/expressions/patterns.rs | 12 +- src/expressions/statements.rs | 2 +- .../type_resolution/interface_macros.rs | 1 - src/expressions/values/array.rs | 27 +-- src/expressions/values/boolean.rs | 54 +---- src/expressions/values/character.rs | 54 +---- src/expressions/values/float.rs | 27 +-- src/expressions/values/float_subtypes.rs | 12 +- src/expressions/values/float_untyped.rs | 9 +- src/expressions/values/integer.rs | 73 ++---- src/expressions/values/integer_subtypes.rs | 7 - src/expressions/values/integer_untyped.rs | 7 - src/expressions/values/iterable.rs | 5 +- src/expressions/values/iterator.rs | 11 +- src/expressions/values/none.rs | 9 +- src/expressions/values/object.rs | 14 +- src/expressions/values/parser.rs | 113 ++++----- src/expressions/values/range.rs | 3 +- src/expressions/values/stream.rs | 96 +++----- src/expressions/values/string.rs | 60 +---- src/expressions/values/unsupported_literal.rs | 19 +- src/expressions/values/value.rs | 216 +++++++----------- src/interpretation/bindings.rs | 2 +- src/interpretation/output_stream.rs | 6 +- 44 files changed, 377 insertions(+), 635 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 11980c28..dfc439cb 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -223,10 +223,25 @@ First, read the @./2025-11-vision.md - [x] Remove `temp_type_data: $type_data:ident,` - [x] Remove `HierarchicalTypeData` - [ ] Stage 1 of the form migration: - - [ ] Replace `Owned` as type reference to `Actual<..>` + - [x] Add temp blanket impl from `IntoValue` for `IntoValueContent` + - [x] Get rid of `BooleanValue` wrapper + - [x] Get rid of `StreamValue` wrapper + - [x] Get rid of `CharValue` wrapper + - [x] Replace `IntegerValue` with `type IntegerValue = IntegerContent<'static, BeOwned>` + - [x] Replace `FloatValue` with `type FloatValue = FloatContent<'static, BeOwned>` + - [x] Replace `Value` with `type Value = ValueContent<'static, BeOwned>` + - [x] Get rid of the `Actual` wrapper inside content + - [ ] --- + - [ ] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` + - [ ] .. same for int... + - [ ] Change `type Value<'a, F> = ValueContent<'a, F>` and `type OwnedValue` with: + - [ ] `type OwnedValue = QqqOwned` + - [ ] `type ValueRef = QqqRef` + - [ ] `type ValueMut = QqqMut` + - [ ] ... and move methods + - [ ] Replace `Owned` as `QqqOwned` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` - [ ] And similarly for other values... - - [ ] Strip wrapper types like `StreamValue` - can just use `OutputStream` as content - [ ] Stage 2 of the form migration: - [ ] Migrate `Shared`, `Mutable`, `Assignee` - [ ] Stage 3 diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs index 6d70970d..92e5902f 100644 --- a/src/expressions/concepts/actual.rs +++ b/src/expressions/concepts/actual.rs @@ -1,5 +1,6 @@ use super::*; +#[derive(Copy, Clone)] pub(crate) struct Actual<'a, T: IsType, F: IsFormOf>(pub(crate) F::Content<'a>); impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> { @@ -124,6 +125,27 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { { Actual::of(self) } + + #[inline] + fn into_actual_value(self) -> Actual<'a, ValueType, Self::Form> + where + Self: Sized, + Self::Type: UpcastTo, + Self::Form: IsFormOf, + { + Actual::of(self).into_value() + } +} + +// TODO[concepts]: Remove eventually, along with IntoValue impl +impl> IntoValue for X +where + X::Type: UpcastTo, + BeOwned: IsFormOf, +{ + fn into_value(self) -> Value { + self.into_actual_value().0 + } } pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 90c08c64..a4867562 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -7,7 +7,11 @@ use super::*; /// - [BeShared] representing [Shared] references /// - [BeMutable] representing [Mutable] references /// - [BeCopyOnWrite] representing [CopyOnWrite] values -pub(crate) trait IsForm: Sized {} +/// +/// NB: The Sized + Clone bounds are just to make certain derive impls easier, +/// due to e.g. the poor auto-derive of Clone which requires Clone bounds on all +/// generics. +pub(crate) trait IsForm: Sized + Clone {} pub(crate) trait IsFormOf: IsForm { type Content<'a>; diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index fea565e9..d3172b72 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -1,5 +1,6 @@ use super::*; +#[derive(Copy, Clone)] pub(crate) struct BeAnyMut; impl IsForm for BeAnyMut {} impl IsHierarchicalForm for BeAnyMut { diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index c4b299c4..376c9624 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -2,6 +2,7 @@ use super::*; type QqqAnyRef<'a, T> = Actual<'a, T, BeAnyRef>; +#[derive(Copy, Clone)] pub(crate) struct BeAnyRef; impl IsForm for BeAnyRef {} diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index 32759f86..b1116635 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -2,6 +2,7 @@ use super::*; pub(crate) type QqqArgumentValue = Actual<'static, T, BeArgument>; +#[derive(Copy, Clone)] pub(crate) struct BeArgument; impl IsForm for BeArgument {} diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 66a812b9..807f8421 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -2,6 +2,7 @@ use super::*; type QqqAssignee = Actual<'static, T, BeAssignee>; +#[derive(Copy, Clone)] pub(crate) struct BeAssignee; impl IsForm for BeAssignee {} diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index b895dd0e..f47ef8c1 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -2,6 +2,7 @@ use super::*; pub(crate) type QqqCopyOnWrite = Actual<'static, T, BeCopyOnWrite>; +#[derive(Copy, Clone)] pub(crate) struct BeCopyOnWrite; impl IsForm for BeCopyOnWrite {} diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index c3ccb86e..838e1035 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -12,6 +12,7 @@ use super::*; /// So instead, we take the most powerful access we can have for `x[a]`, and convert it later. pub(crate) type QqqLateBound = Actual<'static, T, BeLateBound>; +#[derive(Copy, Clone)] pub(crate) struct BeLateBound; impl IsForm for BeLateBound {} diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index f38d6570..7e50741f 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -2,6 +2,7 @@ use super::*; pub(crate) type QqqMutable = Actual<'static, T, BeMutable>; +#[derive(Copy, Clone)] pub(crate) struct BeMutable; impl IsForm for BeMutable {} diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index d3fc4762..fa45f35d 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -1,7 +1,17 @@ use super::*; +/// A semantic wrapper for floating owned values. +/// +/// Can be destructured as: `Owned(value): Owned` +/// +/// If you need span information, wrap with `Spanned>`. For example, for `x.y[4]`, this would capture both: +/// * The output owned value +/// * The lexical span of the tokens `x.y[4]` pub(crate) type QqqOwned = Actual<'static, T, BeOwned>; +pub(crate) type QqqOwnedValue = Owned; + +#[derive(Copy, Clone)] pub(crate) struct BeOwned; impl IsForm for BeOwned {} diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index cd5e7215..13e8e8b9 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -7,6 +7,7 @@ type QqqReferenceable = Actual<'static, T, BeReferenceable>; /// /// Note that Referenceable form does not support dyn casting, because Rc> cannot be /// directly cast to Rc>. +#[derive(Copy, Clone)] pub(crate) struct BeReferenceable; impl IsForm for BeReferenceable {} diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index ce494681..885f43c0 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -2,6 +2,7 @@ use super::*; pub(crate) type QqqShared = Actual<'static, T, BeShared>; +#[derive(Copy, Clone)] pub(crate) struct BeShared; impl IsForm for BeShared {} diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index 64032093..345cd101 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -5,6 +5,7 @@ pub(crate) type QqqMut<'a, T> = Actual<'a, T, BeMut>; /// It can't be an argument because arguments must be owned in some way; /// so that the drop glue can work properly (because they may come from /// e.g. a reference counted Shared handle) +#[derive(Copy, Clone)] pub(crate) struct BeMut; impl IsForm for BeMut {} diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 82819cb0..ec52dde6 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -5,6 +5,7 @@ pub(crate) type QqqRef<'a, T> = Actual<'a, T, BeRef>; /// It can't be an argument because arguments must be owned in some way; /// so that the drop glue can work properly (because they may come from /// e.g. a reference counted Shared handle) +#[derive(Copy, Clone)] pub(crate) struct BeRef; impl IsForm for BeRef {} diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 6c771e89..f4a23067 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -194,14 +194,14 @@ macro_rules! impl_ancestor_chain_conversions { fn into_parent<'a, F: IsHierarchicalForm>( content: Self::Content<'a, F>, ) -> ::Content<'a, F> { - $parent_content::$parent_variant(Actual(content)) + $parent_content::$parent_variant(content) } fn from_parent<'a, F: IsHierarchicalForm>( content: ::Content<'a, F>, ) -> Option> { match content { - $parent_content::$parent_variant(i) => Some(i.0), + $parent_content::$parent_variant(i) => Some(i), _ => None, } } @@ -316,6 +316,7 @@ macro_rules! define_parent_type { type_name: $source_type_name:literal, articled_display_name: $articled_display_name:literal, ) => { + #[derive(Copy, Clone)] $type_def_vis struct $type_def; impl IsType for $type_def { @@ -356,7 +357,7 @@ macro_rules! define_parent_type { content: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { Ok(match content { - $( $content::$variant(x) => $content::$variant(x.map_with::()?), )* + $( $content::$variant(x) => $content::$variant(<$variant_type>::map_with::<'a, F, M>(x)?), )* }) } @@ -364,7 +365,7 @@ macro_rules! define_parent_type { content: &'r Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { Ok(match content { - $( $content::$variant(x) => $content::$variant(x.map_ref_with::()?), )* + $( $content::$variant(x) => $content::$variant(<$variant_type>::map_ref_with::<'r, 'a, F, M>(x)?), )* }) } @@ -372,7 +373,7 @@ macro_rules! define_parent_type { content: &'r mut Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { Ok(match content { - $( $content::$variant(x) => $content::$variant(x.map_mut_with::()?), )* + $( $content::$variant(x) => $content::$variant(<$variant_type>::map_mut_with::<'r, 'a, F, M>(x)?), )* }) } @@ -384,7 +385,41 @@ macro_rules! define_parent_type { } $content_vis enum $content<'a, F: IsHierarchicalForm> { - $( $variant(Actual<'a, $variant_type, F>), )* + $( $variant(>::Content<'a>), )* + } + + impl<'a, F: IsHierarchicalForm> Clone for $content<'a, F> + where + $( >::Content<'a>: Clone ),* + { + fn clone(&self) -> Self { + match self { + $( $content::$variant(x) => $content::$variant(x.clone()), )* + } + } + } + + + impl<'a, F: IsHierarchicalForm> Copy for $content<'a, F> + where + $( >::Content<'a>: Copy ),* + {} + + impl<'a, F: IsHierarchicalForm> IsValueContent<'a> for $content<'a, F> { + type Type = $type_def; + type Form = F; + } + + impl<'a, F: IsHierarchicalForm> IntoValueContent<'a> for $content<'a, F> { + fn into_content(self) -> Self { + self + } + } + + impl<'a, F: IsHierarchicalForm> FromValueContent<'a> for $content<'a, F> { + fn from_content(content: Self) -> Self { + content + } } impl<'a, F: IsHierarchicalForm> HasLeafKind for $content<'a, F> { @@ -393,7 +428,7 @@ macro_rules! define_parent_type { fn kind(&self) -> Self::LeafKind { match self { $($content::$variant(x) => $leaf_kind::$variant( - <$variant_type as IsHierarchicalType>::content_to_leaf_kind::(&x.0) + <$variant_type as IsHierarchicalType>::content_to_leaf_kind::(x) ),)* } } @@ -481,6 +516,7 @@ macro_rules! define_leaf_type { $($dyn_type:ty: impl $dyn_trait:ident { $($dyn_trait_impl:tt)* })* }, ) => { + #[derive(Copy, Clone)] $type_def_vis struct $type_def; impl IsType for $type_def { @@ -571,11 +607,6 @@ macro_rules! define_leaf_type { } } - impl<'a> IsValueContent<'a> for $content_type { - type Type = $type_def; - type Form = BeOwned; - } - impl HasLeafKind for $content_type { type LeafKind = $kind; @@ -584,6 +615,11 @@ macro_rules! define_leaf_type { } } + impl<'a> IsValueContent<'a> for $content_type { + type Type = $type_def; + type Form = BeOwned; + } + impl<'a> IntoValueContent<'a> for $content_type { fn into_content(self) -> Self { self @@ -596,6 +632,40 @@ macro_rules! define_leaf_type { } } + impl<'a> IsValueContent<'a> for &'a $content_type { + type Type = $type_def; + type Form = BeRef; + } + + impl<'a> IntoValueContent<'a> for &'a $content_type { + fn into_content(self) -> Self { + self + } + } + + impl<'a> FromValueContent<'a> for &'a $content_type { + fn from_content(content: Self) -> Self { + content + } + } + + impl<'a> IsValueContent<'a> for &'a mut $content_type { + type Type = $type_def; + type Form = BeMut; + } + + impl<'a> IntoValueContent<'a> for &'a mut $content_type { + fn into_content(self) -> Self { + self + } + } + + impl<'a> FromValueContent<'a> for &'a mut $content_type { + fn from_content(content: Self) -> Self { + content + } + } + impl IsValueLeaf for $content_type {} $( @@ -634,6 +704,7 @@ macro_rules! define_dyn_type { type_name: $source_type_name:literal, articled_display_name: $articled_display_name:literal, ) => { + #[derive(Copy, Clone)] $type_def_vis struct $type_def; impl IsType for $type_def { diff --git a/src/expressions/control_flow.rs b/src/expressions/control_flow.rs index e70eef6d..853d2da7 100644 --- a/src/expressions/control_flow.rs +++ b/src/expressions/control_flow.rs @@ -126,7 +126,7 @@ impl Evaluate for IfExpression { } requested_ownership - .map_from_owned(Spanned(Value::None.into_owned(), self.span_range())) + .map_from_owned(Spanned(().into_owned_value(), self.span_range())) .map(|spanned| spanned.0) } } diff --git a/src/expressions/evaluation/assignment_frames.rs b/src/expressions/evaluation/assignment_frames.rs index c9d8de9b..3a8ba10c 100644 --- a/src/expressions/evaluation/assignment_frames.rs +++ b/src/expressions/evaluation/assignment_frames.rs @@ -300,7 +300,7 @@ impl ObjectBasedAssigner { .entries .remove(&key) .map(|entry| entry.value) - .unwrap_or_else(|| Value::None); + .unwrap_or_else(|| ().into_value()); self.already_used_keys.insert(key); Ok(value) } diff --git a/src/expressions/expression_block.rs b/src/expressions/expression_block.rs index 9b4b8a9f..f7686874 100644 --- a/src/expressions/expression_block.rs +++ b/src/expressions/expression_block.rs @@ -332,6 +332,6 @@ impl ExpressionBlockContent { statement.evaluate_as_statement(interpreter)?; } } - ownership.map_from_owned(Spanned(Owned(Value::None), output_span)) + ownership.map_from_owned(Spanned(().into_owned_value(), output_span)) } } diff --git a/src/expressions/expression_parsing.rs b/src/expressions/expression_parsing.rs index edc0f062..3e27932d 100644 --- a/src/expressions/expression_parsing.rs +++ b/src/expressions/expression_parsing.rs @@ -121,7 +121,7 @@ impl<'a> ExpressionParser<'a> { "true" | "false" => { let bool = input.parse::()?; UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(BooleanValue::for_litbool(&bool).into_owned_value()), + SharedValue::new_from_owned(bool.value.into_owned_value()), bool.span.span_range(), ))) } @@ -134,7 +134,7 @@ impl<'a> ExpressionParser<'a> { "None" => { let none_ident = input.parse_any_ident()?; // consume the "None" token UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(Value::None.into_owned_value()), + SharedValue::new_from_owned(().into_owned_value()), none_ident.span().span_range(), ))) } diff --git a/src/expressions/patterns.rs b/src/expressions/patterns.rs index 3937bfde..2a471ad4 100644 --- a/src/expressions/patterns.rs +++ b/src/expressions/patterns.rs @@ -253,7 +253,7 @@ impl HandleDestructure for ObjectPattern { let value = value_map .remove(&key) .map(|entry| entry.value) - .unwrap_or_else(|| Value::None); + .unwrap_or_else(|| ().into_value()); already_used_keys.insert(key); pattern.handle_destructure(interpreter, value)?; } @@ -354,11 +354,9 @@ impl HandleDestructure for StreamPattern { interpreter: &mut Interpreter, value: Value, ) -> ExecutionResult<()> { - let stream: StreamValue = Spanned(value.into_owned(), self.brackets.span_range()) + let stream = Spanned(Actual::of(value), self.brackets.span_range()) .resolve_as("The value destructured with a stream pattern")?; - interpreter.start_parse(stream.value, |interpreter, _| { - self.content.consume(interpreter) - }) + interpreter.start_parse(stream, |interpreter, _| self.content.consume(interpreter)) } } @@ -398,9 +396,9 @@ impl HandleDestructure for ParseTemplatePattern { interpreter: &mut Interpreter, value: Value, ) -> ExecutionResult<()> { - let stream: StreamValue = Spanned(value.into_owned(), self.brackets.span_range()) + let stream = Spanned(Actual::of(value), self.brackets.span_range()) .resolve_as("The value destructured with a parse template pattern")?; - interpreter.start_parse(stream.value, |interpreter, handle| { + interpreter.start_parse(stream, |interpreter, handle| { self.parser_definition.define(interpreter, handle); self.content.consume(interpreter) }) diff --git a/src/expressions/statements.rs b/src/expressions/statements.rs index dcdafc5f..ee45464f 100644 --- a/src/expressions/statements.rs +++ b/src/expressions/statements.rs @@ -145,7 +145,7 @@ impl LetStatement { } = self; let value = match assignment { Some(assignment) => assignment.expression.evaluate_owned(interpreter)?.0 .0, - None => Value::None, + None => ().into_value(), }; pattern.handle_destructure(interpreter, value)?; Ok(()) diff --git a/src/expressions/type_resolution/interface_macros.rs b/src/expressions/type_resolution/interface_macros.rs index 3bbe2ae9..4caa3528 100644 --- a/src/expressions/type_resolution/interface_macros.rs +++ b/src/expressions/type_resolution/interface_macros.rs @@ -343,7 +343,6 @@ impl<'a> BinaryOperationCallContext<'a> { macro_rules! define_type_features { ( impl $type_def:ident, - parent: $parent_type_def:ident, $mod_vis:vis mod $mod_name:ident { $mod_methods_vis:vis mod methods { $( diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 4754dac3..02311670 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -45,12 +45,12 @@ impl ArrayValue { Spanned(index, span_range): Spanned<&Value>, ) -> ExecutionResult { Ok(match index { - Value::Integer(integer) => { + ValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; - std::mem::replace(&mut self.items[index], Value::None) + std::mem::replace(&mut self.items[index], ().into_value()) } - Value::Range(range) => { + ValueContent::Range(range) => { let range = Spanned(range, span_range).resolve_to_index_range(&self)?; let new_items: Vec<_> = self.items.drain(range).collect(); new_items.into_value() @@ -64,12 +64,12 @@ impl ArrayValue { Spanned(index, span_range): Spanned<&Value>, ) -> ExecutionResult<&mut Value> { Ok(match index { - Value::Integer(integer) => { + ValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; &mut self.items[index] } - Value::Range(..) => { + ValueContent::Range(..) => { // Temporary until we add slice types - we error here return span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]"); } @@ -82,12 +82,12 @@ impl ArrayValue { Spanned(index, span_range): Spanned<&Value>, ) -> ExecutionResult<&Value> { Ok(match index { - Value::Integer(integer) => { + ValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; &self.items[index] } - Value::Range(..) => { + ValueContent::Range(..) => { // Temporary until we add slice types - we error here return span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]"); } @@ -101,7 +101,7 @@ impl ArrayValue { is_exclusive: bool, ) -> ExecutionResult { match index { - Value::Integer(int) => { + ValueContent::Integer(int) => { self.resolve_valid_index_from_integer(Spanned(int, span_range), is_exclusive) } _ => span_range.type_err("The index must be an integer"), @@ -171,13 +171,7 @@ impl ValuesEqual for ArrayValue { impl IntoValue for Vec { fn into_value(self) -> Value { - Value::Array(ArrayValue { items: self }) - } -} - -impl IntoValue for ArrayValue { - fn into_value(self) -> Value { - Value::Array(self) + ArrayValue { items: self }.into_value() } } @@ -185,7 +179,7 @@ impl_resolvable_argument_for! { ArrayType, (value, context) -> ArrayValue { match value { - Value::Array(value) => Ok(value), + ValueContent::Array(value) => Ok(value), _ => context.err("an array", value), } } @@ -193,7 +187,6 @@ impl_resolvable_argument_for! { define_type_features! { impl ArrayType, - parent: IterableType, pub(crate) mod array_interface { pub(crate) mod methods { fn push(mut this: Mutable, item: OwnedValue) -> ExecutionResult<()> { diff --git a/src/expressions/values/boolean.rs b/src/expressions/values/boolean.rs index 2fad22d1..6711f22f 100644 --- a/src/expressions/values/boolean.rs +++ b/src/expressions/values/boolean.rs @@ -11,44 +11,9 @@ define_leaf_type! { dyn_impls: {}, } -#[derive(Clone)] -pub(crate) struct BooleanValue { - pub(crate) value: bool, -} - -impl IntoValue for BooleanValue { - fn into_value(self) -> Value { - Value::Boolean(self) - } -} - -impl Debug for BooleanValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.value) - } -} - -impl BooleanValue { - pub(crate) fn for_litbool(lit: &syn::LitBool) -> Owned { - Self { value: lit.value }.into_owned() - } - - pub(super) fn to_ident(&self, span: Span) -> Ident { - Ident::new_bool(self.value, span) - } -} - -impl HasLeafKind for BooleanValue { - type LeafKind = BoolKind; - - fn kind(&self) -> Self::LeafKind { - BoolKind - } -} - -impl ValuesEqual for BooleanValue { +impl ValuesEqual for bool { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.value == other.value { + if self == other { ctx.values_equal() } else { ctx.leaf_values_not_equal(self, other) @@ -56,15 +21,8 @@ impl ValuesEqual for BooleanValue { } } -impl IntoValue for bool { - fn into_value(self) -> Value { - Value::Boolean(BooleanValue { value: self }) - } -} - define_type_features! { impl BoolType, - parent: ValueType, pub(crate) mod boolean_interface { pub(crate) mod methods { } @@ -227,14 +185,10 @@ define_type_features! { impl_resolvable_argument_for! { BoolType, - (value, context) -> BooleanValue { + (value, context) -> bool { match value { - Value::Boolean(value) => Ok(value), + ValueContent::Bool(value) => Ok(value), other => context.err("a bool", other), } } } - -impl_delegated_resolvable_argument_for! { - (value: BooleanValue) -> bool { value.value } -} diff --git a/src/expressions/values/character.rs b/src/expressions/values/character.rs index 16d0364c..426bc529 100644 --- a/src/expressions/values/character.rs +++ b/src/expressions/values/character.rs @@ -9,44 +9,9 @@ define_leaf_type! { dyn_impls: {}, } -#[derive(Clone)] -pub(crate) struct CharValue { - pub(super) value: char, -} - -impl IntoValue for CharValue { - fn into_value(self) -> Value { - Value::Char(self) - } -} - -impl Debug for CharValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.value) - } -} - -impl CharValue { - pub(super) fn for_litchar(lit: &syn::LitChar) -> Owned { - Self { value: lit.value() }.into_owned() - } - - pub(super) fn to_literal(&self, span: Span) -> Literal { - Literal::character(self.value).with_span(span) - } -} - -impl HasLeafKind for CharValue { - type LeafKind = CharKind; - - fn kind(&self) -> Self::LeafKind { - CharKind - } -} - -impl ValuesEqual for CharValue { +impl ValuesEqual for char { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.value == other.value { + if self == other { ctx.values_equal() } else { ctx.leaf_values_not_equal(self, other) @@ -54,15 +19,8 @@ impl ValuesEqual for CharValue { } } -impl IntoValue for char { - fn into_value(self) -> Value { - Value::Char(CharValue { value: self }) - } -} - define_type_features! { impl CharType, - parent: ValueType, pub(crate) mod char_interface { pub(crate) mod methods { } @@ -196,14 +154,10 @@ define_type_features! { impl_resolvable_argument_for! { CharType, - (value, context) -> CharValue { + (value, context) -> char { match value { - Value::Char(value) => Ok(value), + ValueContent::Char(char) => Ok(char), _ => context.err("a char", value), } } } - -impl_delegated_resolvable_argument_for!( - (value: CharValue) -> char { value.value } -); diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index d1ddf7b6..c25ee63f 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -14,18 +14,7 @@ define_parent_type! { articled_display_name: "a float", } -#[derive(Copy, Clone)] -pub(crate) enum FloatValue { - Untyped(UntypedFloat), - F32(f32), - F64(f64), -} - -impl IntoValue for FloatValue { - fn into_value(self) -> Value { - Value::Float(self) - } -} +pub(crate) type FloatValue = FloatContent<'static, BeOwned>; impl FloatValue { pub(super) fn for_litfloat(lit: &syn::LitFloat) -> ParseResult> { @@ -129,19 +118,6 @@ impl Debug for FloatValue { } } -// TODO[concepts]: Remove when this is auto-generated after Value changes -impl HasLeafKind for FloatValue { - type LeafKind = FloatLeafKind; - - fn kind(&self) -> FloatLeafKind { - match self { - FloatValue::Untyped(_) => FloatLeafKind::Untyped(UntypedFloatKind), - FloatValue::F32(_) => FloatLeafKind::F32(F32Kind), - FloatValue::F64(_) => FloatLeafKind::F64(F64Kind), - } - } -} - impl FloatValue { /// Aligns types for comparison - converts untyped to match the other's type. /// Unlike integers, float conversion never fails (may lose precision). @@ -190,7 +166,6 @@ impl ValuesEqual for FloatValue { define_type_features! { impl FloatType, - parent: ValueType, pub(crate) mod float_interface { pub(crate) mod methods { fn is_nan(this: FloatValue) -> bool { diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 769d2ceb..6844fb5c 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -6,7 +6,6 @@ macro_rules! impl_float_operations { ) => {$( define_type_features! { impl $type_data, - parent: FloatType, pub(crate) mod $mod_name { pub(crate) mod methods { } @@ -138,13 +137,6 @@ macro_rules! impl_float_operations { } } - impl IntoValue for $float_type { - fn into_value(self) -> Value { - Value::Float(FloatValue::$float_enum_variant(self)) - } - } - - impl HandleBinaryOperation for $float_type { fn type_name() -> &'static str { stringify!($integer_type) @@ -207,7 +199,7 @@ macro_rules! impl_resolvable_float_subtype { context: ResolutionContext, ) -> ExecutionResult<&'a Self> { match value { - Value::Float(FloatValue::$variant(x)) => Ok(x), + ValueContent::Float(FloatContent::$variant(x)) => Ok(x), other => context.err($articled_display_name, other), } } @@ -219,7 +211,7 @@ macro_rules! impl_resolvable_float_subtype { context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { match value { - Value::Float(FloatValue::$variant(x)) => Ok(x), + ValueContent::Float(FloatContent::$variant(x)) => Ok(x), other => context.err($articled_display_name, other), } } diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 10828bb0..5abbac09 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -70,15 +70,8 @@ impl UntypedFloat { } } -impl IntoValue for UntypedFloat { - fn into_value(self) -> Value { - Value::Float(FloatValue::Untyped(self)) - } -} - define_type_features! { impl UntypedFloatType, - parent: FloatType, pub(crate) mod untyped_float_interface { pub(crate) mod methods { } @@ -221,7 +214,7 @@ impl_resolvable_argument_for! { UntypedFloatType, (value, context) -> UntypedFloat { match value { - Value::Float(FloatValue::Untyped(x)) => Ok(x), + ValueContent::Float(FloatValue::Untyped(x)) => Ok(x), other => context.err("an untyped float", other), } } diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index db16ea89..769963ea 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -24,45 +24,24 @@ define_parent_type! { articled_display_name: "an integer", } -#[derive(Copy, Clone)] -pub(crate) enum IntegerValue { - Untyped(UntypedInteger), - U8(u8), - U16(u16), - U32(u32), - U64(u64), - U128(u128), - Usize(usize), - I8(i8), - I16(i16), - I32(i32), - I64(i64), - I128(i128), - Isize(isize), -} - -impl IntoValue for IntegerValue { - fn into_value(self) -> Value { - Value::Integer(self) - } -} +pub(crate) type IntegerValue = IntegerContent<'static, BeOwned>; impl IntegerValue { pub(super) fn for_litint(lit: &syn::LitInt) -> ParseResult> { Ok(match lit.suffix() { - "" => Self::Untyped(UntypedInteger::new_from_lit_int(lit)?), - "u8" => Self::U8(lit.base10_parse()?), - "u16" => Self::U16(lit.base10_parse()?), - "u32" => Self::U32(lit.base10_parse()?), - "u64" => Self::U64(lit.base10_parse()?), - "u128" => Self::U128(lit.base10_parse()?), - "usize" => Self::Usize(lit.base10_parse()?), - "i8" => Self::I8(lit.base10_parse()?), - "i16" => Self::I16(lit.base10_parse()?), - "i32" => Self::I32(lit.base10_parse()?), - "i64" => Self::I64(lit.base10_parse()?), - "i128" => Self::I128(lit.base10_parse()?), - "isize" => Self::Isize(lit.base10_parse()?), + "" => IntegerContent::Untyped(UntypedInteger::new_from_lit_int(lit)?), + "u8" => IntegerContent::U8(lit.base10_parse()?), + "u16" => IntegerContent::U16(lit.base10_parse()?), + "u32" => IntegerContent::U32(lit.base10_parse()?), + "u64" => IntegerContent::U64(lit.base10_parse()?), + "u128" => IntegerContent::U128(lit.base10_parse()?), + "usize" => IntegerContent::Usize(lit.base10_parse()?), + "i8" => IntegerContent::I8(lit.base10_parse()?), + "i16" => IntegerContent::I16(lit.base10_parse()?), + "i32" => IntegerContent::I32(lit.base10_parse()?), + "i64" => IntegerContent::I64(lit.base10_parse()?), + "i128" => IntegerContent::I128(lit.base10_parse()?), + "isize" => IntegerContent::Isize(lit.base10_parse()?), suffix => { return lit.span().parse_err(format!( "The literal suffix {suffix} is not supported in preinterpret expressions" @@ -133,29 +112,6 @@ impl IntegerValue { } } -// TODO[concepts]: Remove when this is auto-generated after Value changes -impl HasLeafKind for IntegerValue { - type LeafKind = IntegerLeafKind; - - fn kind(&self) -> IntegerLeafKind { - match self { - IntegerValue::Untyped(_) => IntegerLeafKind::Untyped(UntypedIntegerKind), - IntegerValue::U8(_) => IntegerLeafKind::U8(U8Kind), - IntegerValue::U16(_) => IntegerLeafKind::U16(U16Kind), - IntegerValue::U32(_) => IntegerLeafKind::U32(U32Kind), - IntegerValue::U64(_) => IntegerLeafKind::U64(U64Kind), - IntegerValue::U128(_) => IntegerLeafKind::U128(U128Kind), - IntegerValue::Usize(_) => IntegerLeafKind::Usize(UsizeKind), - IntegerValue::I8(_) => IntegerLeafKind::I8(I8Kind), - IntegerValue::I16(_) => IntegerLeafKind::I16(I16Kind), - IntegerValue::I32(_) => IntegerLeafKind::I32(I32Kind), - IntegerValue::I64(_) => IntegerLeafKind::I64(I64Kind), - IntegerValue::I128(_) => IntegerLeafKind::I128(I128Kind), - IntegerValue::Isize(_) => IntegerLeafKind::Isize(IsizeKind), - } - } -} - impl Debug for IntegerValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -246,7 +202,6 @@ impl ValuesEqual for IntegerValue { define_type_features! { impl IntegerType, - parent: ValueType, pub(crate) mod integer_interface { pub(crate) mod methods { } diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 53c5a09c..43492c9d 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -7,7 +7,6 @@ macro_rules! impl_int_operations { ) => {$( define_type_features! { impl $integer_type_data, - parent: IntegerType, pub(crate) mod $mod_name { pub(crate) mod methods { } @@ -159,12 +158,6 @@ macro_rules! impl_int_operations { } } - impl IntoValue for $integer_type { - fn into_value(self) -> Value { - Value::Integer(IntegerValue::$integer_enum_variant(self)) - } - } - impl HandleBinaryOperation for $integer_type { fn type_name() -> &'static str { stringify!($integer_type) diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index d20b2cf4..44351f43 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -121,15 +121,8 @@ impl Spanned { } } -impl IntoValue for UntypedInteger { - fn into_value(self) -> Value { - Value::Integer(IntegerValue::Untyped(self)) - } -} - define_type_features! { impl UntypedIntegerType, - parent: IntegerType, pub(crate) mod untyped_integer_interface { pub(crate) mod methods { } diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index 8f812c62..c210eff6 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -35,10 +35,10 @@ impl ResolvableOwned for IterableValue { Ok(match value { Value::Array(x) => Self::Array(x), Value::Object(x) => Self::Object(x), - Value::Stream(x) => Self::Stream(x.value), + Value::Stream(x) => Self::Stream(x), Value::Range(x) => Self::Range(x), Value::Iterator(x) => Self::Iterator(x), - Value::String(x) => Self::String(x.value), + Value::String(x) => Self::String(x), _ => { return context.err( "an iterable (iterator, array, object, stream, range or string)", @@ -51,7 +51,6 @@ impl ResolvableOwned for IterableValue { define_type_features! { impl IterableType, - parent: ValueType, pub(crate) mod iterable_interface { pub(crate) mod methods { fn into_iter(this: IterableValue) -> ExecutionResult { diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index 99057912..c70ab855 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -195,14 +195,6 @@ impl IntoValue for Box> { } } -impl IntoValue for IteratorValue { - fn into_value(self) -> Value { - Value::Iterator(IteratorValue { - iterator: self.iterator, - }) - } -} - impl_resolvable_argument_for! { IteratorType, (value, context) -> IteratorValue { @@ -300,13 +292,12 @@ impl Iterator for Mutable { define_type_features! { impl IteratorType, - parent: IterableType, pub(crate) mod iterator_interface { pub(crate) mod methods { fn next(mut this: Mutable) -> Value { match this.next() { Some(value) => value, - None => Value::None, + None => ().into_value(), } } diff --git a/src/expressions/values/none.rs b/src/expressions/values/none.rs index 47ad2d66..473a2f45 100644 --- a/src/expressions/values/none.rs +++ b/src/expressions/values/none.rs @@ -10,12 +10,6 @@ define_leaf_type! { dyn_impls: {}, } -impl IntoValue for () { - fn into_value(self) -> Value { - Value::None - } -} - impl ResolvableArgumentTarget for () { type ValueType = NoneType; } @@ -23,7 +17,7 @@ impl ResolvableArgumentTarget for () { impl ResolvableOwned for () { fn resolve_from_value(value: Value, context: ResolutionContext) -> ExecutionResult { match value { - Value::None => Ok(()), + Value::None(_) => Ok(()), other => context.err("None", other), } } @@ -37,7 +31,6 @@ define_optional_object! { define_type_features! { impl NoneType, - parent: ValueType, pub(crate) mod none_interface { pub(crate) mod methods { [context] fn configure_preinterpret(_none: (), inputs: SettingsInputs) { diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index 9cb4afdd..557dcbe4 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -24,12 +24,6 @@ pub(crate) struct ObjectValue { pub(crate) entries: BTreeMap, } -impl IntoValue for ObjectValue { - fn into_value(self) -> Value { - Value::Object(self) - } -} - impl_resolvable_argument_for! { ObjectType, (value, context) -> ObjectValue { @@ -61,7 +55,7 @@ impl ObjectValue { pub(crate) fn remove_or_none(&mut self, key: &str) -> Value { match self.entries.remove(key) { Some(entry) => entry.value, - None => Value::None, + None => ().into_value(), } } @@ -127,7 +121,7 @@ impl ObjectValue { &mut entry .insert(ObjectEntry { key_span: key_span.join_into_span_else_start(), - value: Value::None, + value: ().into_value(), }) .value } else { @@ -221,7 +215,8 @@ impl Spanned<&ObjectValue> { match self.entries.get(field_name) { None | Some(ObjectEntry { - value: Value::None, .. + value: Value::None(_), + .. }) => { missing_fields.push(field_name); } @@ -268,7 +263,6 @@ impl IntoValue for BTreeMap { define_type_features! { impl ObjectType, - parent: IterableType, pub(crate) mod object_interface { pub(crate) mod methods { [context] fn zip(this: ObjectValue) -> ExecutionResult { diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index a57c0799..11be17b4 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -9,28 +9,11 @@ define_leaf_type! { dyn_impls: {}, } -#[derive(Clone)] -pub(crate) struct ParserValue { - handle: ParserHandle, -} - -impl HasLeafKind for ParserValue { - type LeafKind = ParserKind; - - fn kind(&self) -> Self::LeafKind { - ParserKind - } -} +struct ParserHandleValueWrapper(ParserHandle); -impl Debug for ParserValue { +impl Debug for ParserHandleValueWrapper { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Parser[{:?}]", self.handle) - } -} - -impl ParserValue { - pub(crate) fn new(handle: ParserHandle) -> Self { - Self { handle } + write!(f, "Parser[{:?}]", self.0) } } @@ -52,35 +35,27 @@ fn delimiter_from_close_char(c: char) -> Option { } } -impl ValuesEqual for ParserValue { +impl ValuesEqual for ParserHandle { /// Parsers are equal if they reference the same handle. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.handle == other.handle { + if self == other { ctx.values_equal() } else { - ctx.leaf_values_not_equal(self, other) + ctx.leaf_values_not_equal( + &ParserHandleValueWrapper(*self), + &ParserHandleValueWrapper(*other), + ) } } } -impl IntoValue for ParserValue { - fn into_value(self) -> Value { - Value::Parser(self) - } -} - -impl IntoValue for ParserHandle { - fn into_value(self) -> Value { - ParserValue::new(self).into_value() - } -} - -impl Spanned> { +impl Spanned> { pub(crate) fn parser<'i>( &self, interpreter: &'i mut Interpreter, ) -> ExecutionResult> { - interpreter.parser(self.handle, self.1) + let Spanned(handle, span) = self; + interpreter.parser(**handle, *span) } pub(crate) fn parse_with( @@ -88,12 +63,13 @@ impl Spanned> { interpreter: &mut Interpreter, f: impl FnOnce(&mut Interpreter) -> ExecutionResult, ) -> ExecutionResult { - interpreter.parse_with(self.handle, f) + let Spanned(handle, _) = self; + interpreter.parse_with(**handle, f) } } fn parser<'a>( - this: Spanned>, + this: Spanned>, context: &'a mut MethodCallContext, ) -> ExecutionResult> { this.parser(context.interpreter) @@ -101,18 +77,17 @@ fn parser<'a>( define_type_features! { impl ParserType, - parent: ValueType, pub(crate) mod parser_interface { pub(crate) mod methods { // GENERAL // ======= - [context] fn is_end(this: Spanned>) -> ExecutionResult { + [context] fn is_end(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.is_empty()) } // Asserts that the parser has reached the end of input - [context] fn end(this: Spanned>) -> ExecutionResult<()> { + [context] fn end(this: Spanned>) -> ExecutionResult<()> { let parser = parser(this, context)?; match parser.is_empty() { true => Ok(()), @@ -120,37 +95,37 @@ define_type_features! { } } - [context] fn token_tree(this: Spanned>) -> ExecutionResult { + [context] fn token_tree(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.parse()?) } - [context] fn ident(this: Spanned>) -> ExecutionResult { + [context] fn ident(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.parse()?) } - [context] fn any_ident(this: Spanned>) -> ExecutionResult { + [context] fn any_ident(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.parse_any_ident()?) } - [context] fn punct(this: Spanned>) -> ExecutionResult { + [context] fn punct(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.parse()?) } - [context] fn read(this: Spanned>, parse_template: AnyRef) -> ExecutionResult { + [context] fn read(this: Spanned>, parse_template: AnyRef) -> ExecutionResult { let this = parser(this, context)?; let mut output = OutputStream::new(); parse_template.parse_exact_match(this, &mut output)?; Ok(output) } - [context] fn rest(this: Spanned>) -> ExecutionResult { + [context] fn rest(this: Spanned>) -> ExecutionResult { let input = parser(this, context)?; let mut output = OutputStream::new(); ParseUntil::End.handle_parse_into(input, &mut output)?; Ok(output) } - [context] fn until(this: Spanned>, until: OutputStream) -> ExecutionResult { + [context] fn until(this: Spanned>, until: OutputStream) -> ExecutionResult { let input = parser(this, context)?; let until: ParseUntil = until.parse_as()?; let mut output = OutputStream::new(); @@ -158,7 +133,7 @@ define_type_features! { Ok(output) } - [context] fn error(this: Spanned>, message: String) -> ExecutionResult<()> { + [context] fn error(this: Spanned>, message: String) -> ExecutionResult<()> { let parser = parser(this, context)?; parser.parse_err(message).map_err(|e| e.into()) } @@ -168,7 +143,7 @@ define_type_features! { // Opens a group with the specified delimiter character ('(', '{', or '['). // Must be paired with `close`. - [context] fn open(this: Spanned>, Spanned(delimiter_char, char_span): Spanned>) -> ExecutionResult<()> { + [context] fn open(this: Spanned>, Spanned(delimiter_char, char_span): Spanned>) -> ExecutionResult<()> { let delimiter_char = delimiter_char.into_inner(); let delimiter = delimiter_from_open_char(delimiter_char) .ok_or_else(|| char_span.value_error(format!( @@ -182,7 +157,7 @@ define_type_features! { // Closes the current group. Must be paired with a prior `open`. // The close character must match: ')' for '(', '}' for '{', ']' for '[' - [context] fn close(this: Spanned>, Spanned(delimiter_char, char_span): Spanned>) -> ExecutionResult<()> { + [context] fn close(this: Spanned>, Spanned(delimiter_char, char_span): Spanned>) -> ExecutionResult<()> { let delimiter_char = delimiter_char.into_inner(); let expected_delimiter = delimiter_from_close_char(delimiter_char) .ok_or_else(|| char_span.value_error(format!( @@ -209,72 +184,72 @@ define_type_features! { // LITERALS // ======== - [context] fn is_literal(this: Spanned>) -> ExecutionResult { + [context] fn is_literal(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.cursor().literal().is_some()) } - [context] fn literal(this: Spanned>) -> ExecutionResult { + [context] fn literal(this: Spanned>) -> ExecutionResult { let literal = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_literal(literal))) } - [context] fn inferred_literal(this: Spanned>) -> ExecutionResult { + [context] fn inferred_literal(this: Spanned>) -> ExecutionResult { let literal = parser(this, context)?.parse()?; Ok(Value::for_literal(literal).into_value()) } - [context] fn is_char(this: Spanned>) -> ExecutionResult { + [context] fn is_char(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.peek(syn::LitChar)) } - [context] fn char_literal(this: Spanned>) -> ExecutionResult { + [context] fn char_literal(this: Spanned>) -> ExecutionResult { let char: syn::LitChar = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_tokens(char))) } - [context] fn char(this: Spanned>) -> ExecutionResult { + [context] fn char(this: Spanned>) -> ExecutionResult { let char: syn::LitChar = parser(this, context)?.parse()?; Ok(char.value()) } - [context] fn is_string(this: Spanned>) -> ExecutionResult { + [context] fn is_string(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.peek(syn::LitStr)) } - [context] fn string_literal(this: Spanned>) -> ExecutionResult { + [context] fn string_literal(this: Spanned>) -> ExecutionResult { let string: syn::LitStr = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_tokens(string))) } - [context] fn string(this: Spanned>) -> ExecutionResult { + [context] fn string(this: Spanned>) -> ExecutionResult { let string: syn::LitStr = parser(this, context)?.parse()?; Ok(string.value()) } - [context] fn is_integer(this: Spanned>) -> ExecutionResult { + [context] fn is_integer(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.peek(syn::LitInt)) } - [context] fn integer_literal(this: Spanned>) -> ExecutionResult { + [context] fn integer_literal(this: Spanned>) -> ExecutionResult { let integer: syn::LitInt = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_tokens(integer))) } - [context] fn integer(this: Spanned>) -> ExecutionResult { + [context] fn integer(this: Spanned>) -> ExecutionResult { let integer: syn::LitInt = parser(this, context)?.parse()?; Ok(IntegerValue::for_litint(&integer)?.into_inner()) } - [context] fn is_float(this: Spanned>) -> ExecutionResult { + [context] fn is_float(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.peek(syn::LitFloat)) } - [context] fn float_literal(this: Spanned>) -> ExecutionResult { + [context] fn float_literal(this: Spanned>) -> ExecutionResult { let float: syn::LitFloat = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_tokens(float))) } - [context] fn float(this: Spanned>) -> ExecutionResult { + [context] fn float(this: Spanned>) -> ExecutionResult { let float: syn::LitFloat = parser(this, context)?.parse()?; Ok(FloatValue::for_litfloat(&float)?.into_inner()) } @@ -289,7 +264,7 @@ define_type_features! { impl_resolvable_argument_for! { ParserType, - (value, context) -> ParserValue { + (value, context) -> ParserHandle { match value { Value::Parser(value) => Ok(value), other => context.err("a parser", other), @@ -357,7 +332,7 @@ impl Evaluate for ParseTemplateLiteral { interpreter: &mut Interpreter, ownership: RequestedOwnership, ) -> ExecutionResult { - let parser: Shared = Spanned( + let parser: Shared = Spanned( self.parser_reference.resolve_shared(interpreter)?, self.parser_reference.span_range(), ) diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index 55b27fb8..c7c5e04a 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -361,7 +361,6 @@ impl_resolvable_argument_for! { define_type_features! { impl RangeType, - parent: IterableType, pub(crate) mod range_interface { pub(crate) mod methods { } @@ -461,7 +460,7 @@ impl IterableRangeOf { IntegerValue::Isize(start) => resolve_range(start, dots, end), } } - Value::Char(start) => resolve_range(start.value, dots, end), + Value::Char(start) => resolve_range(start, dots, end), _ => dots.value_err("The range must be between two integers or two characters"), } } diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index 96e66c7b..e8deed1e 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -19,23 +19,18 @@ define_leaf_type! { }, } -#[derive(Clone)] -pub(crate) struct StreamValue { - pub(crate) value: OutputStream, -} - -impl StreamValue { - pub(crate) fn concat_recursive_into(&self, output: &mut String, behaviour: &ConcatBehaviour) { +impl OutputStream { + pub(crate) fn concat_as_literal_into(&self, output: &mut String, behaviour: &ConcatBehaviour) { if behaviour.use_stream_literal_syntax { - if self.value.is_empty() { + if self.is_empty() { output.push_str("%[]"); } else { output.push_str("%["); - self.value.concat_recursive_into(output, behaviour); + self.concat_content_into(output, behaviour); output.push(']'); } } else { - self.value.concat_recursive_into(output, behaviour); + self.concat_content_into(output, behaviour); } } @@ -66,7 +61,7 @@ impl StreamValue { // transparent groups (as of Jan 2025), so gets it right without this flattening: // https://github.com/rust-lang/rust-analyzer/issues/18211 - let error_span_stream = self.value.to_token_stream_removing_any_transparent_groups(); + let error_span_stream = self.to_token_stream_removing_any_transparent_groups(); if error_span_stream.is_empty() { None } else { @@ -75,18 +70,10 @@ impl StreamValue { } } -impl HasLeafKind for StreamValue { - type LeafKind = StreamKind; - - fn kind(&self) -> Self::LeafKind { - StreamKind - } -} - -impl Debug for StreamValue { +impl Debug for OutputStream { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut debug_string = String::new(); - self.concat_recursive_into( + self.concat_as_literal_into( &mut debug_string, &ConcatBehaviour::debug(Span::call_site().span_range()), ); @@ -94,18 +81,14 @@ impl Debug for StreamValue { } } -impl ValuesEqual for StreamValue { +impl ValuesEqual for OutputStream { /// Compares two streams by their debug string representation, ignoring spans. /// Transparent groups (none-delimited groups) are preserved in comparison. /// Use `remove_transparent_groups()` before comparison if you want to ignore them. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { // Use debug concat_recursive which preserves transparent group structure - let lhs = self - .value - .concat_recursive(&ConcatBehaviour::debug(Span::call_site().span_range())); - let rhs = other - .value - .concat_recursive(&ConcatBehaviour::debug(Span::call_site().span_range())); + let lhs = self.concat_content(&ConcatBehaviour::debug(Span::call_site().span_range())); + let rhs = other.concat_content(&ConcatBehaviour::debug(Span::call_site().span_range())); if lhs == rhs { ctx.values_equal() } else { @@ -114,18 +97,6 @@ impl ValuesEqual for StreamValue { } } -impl IntoValue for StreamValue { - fn into_value(self) -> Value { - Value::Stream(self) - } -} - -impl IntoValue for OutputStream { - fn into_value(self) -> Value { - StreamValue { value: self }.into_value() - } -} - impl IntoValue for TokenStream { fn into_value(self) -> Value { OutputStream::raw(self).into_value() @@ -134,21 +105,16 @@ impl IntoValue for TokenStream { impl_resolvable_argument_for! { StreamType, - (value, context) -> StreamValue { + (value, context) -> OutputStream { match value { - Value::Stream(value) => Ok(value), + ValueContent::Stream(value) => Ok(value), _ => context.err("a stream", value), } } } -impl_delegated_resolvable_argument_for!( - (value: StreamValue) -> OutputStream { value.value } -); - define_type_features! { impl StreamType, - parent: IterableType, pub(crate) mod stream_interface { pub(crate) mod methods { // This is also on iterable, but is specialized here for performance @@ -183,28 +149,28 @@ define_type_features! { // =============================== [context] fn to_ident(this: Spanned>) -> ExecutionResult { - let string = this.concat_recursive(&ConcatBehaviour::standard(this.span_range())); + let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); string_interface::methods::to_ident(context, string.as_str().into_spanned_ref(this.span_range())) } [context] fn to_ident_camel(this: Spanned>) -> ExecutionResult { - let string = this.concat_recursive(&ConcatBehaviour::standard(this.span_range())); + let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); string_interface::methods::to_ident_camel(context, string.as_str().into_spanned_ref(this.span_range())) } [context] fn to_ident_snake(this: Spanned>) -> ExecutionResult { - let string = this.concat_recursive(&ConcatBehaviour::standard(this.span_range())); + let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); string_interface::methods::to_ident_snake(context, string.as_str().into_spanned_ref(this.span_range())) } [context] fn to_ident_upper_snake(this: Spanned>) -> ExecutionResult { - let string = this.concat_recursive(&ConcatBehaviour::standard(this.span_range())); + let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); string_interface::methods::to_ident_upper_snake(context, string.as_str().into_spanned_ref(this.span_range())) } // Some literals become Value::UnsupportedLiteral but can still be round-tripped back to a stream [context] fn to_literal(this: Spanned>) -> ExecutionResult { - let string = this.concat_recursive(&ConcatBehaviour::literal(this.span_range())); + let string = this.concat_content(&ConcatBehaviour::literal(this.span_range())); let literal = string_interface::methods::to_literal(context, string.as_str().into_spanned_ref(this.span_range()))?; Ok(Value::for_literal(literal).into_value()) } @@ -213,18 +179,18 @@ define_type_features! { // ============ // NOTE: with_span() exists on all values, this is just a specialized mutable version for streams - fn set_span(mut this: Mutable, span_source: Shared) -> ExecutionResult<()> { + fn set_span(mut this: Mutable, span_source: Shared) -> ExecutionResult<()> { let span_range = span_source.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); - this.value.replace_first_level_spans(span_range.join_into_span_else_start()); + this.replace_first_level_spans(span_range.join_into_span_else_start()); Ok(()) } - fn error(this: Shared, message: Shared) -> ExecutionResult { + fn error(this: Shared, message: Shared) -> ExecutionResult { let error_span_range = this.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); error_span_range.assertion_err(message.as_str()) } - fn assert(this: Shared, condition: bool, message: Option>) -> ExecutionResult<()> { + fn assert(this: Shared, condition: bool, message: Option>) -> ExecutionResult<()> { if condition { Ok(()) } else { @@ -237,7 +203,7 @@ define_type_features! { } } - fn assert_eq(this: Shared, lhs: Spanned>, rhs: Spanned>, message: Option>) -> ExecutionResult<()> { + fn assert_eq(this: Shared, lhs: Spanned>, rhs: Spanned>, message: Option>) -> ExecutionResult<()> { let lhs_value: &Value = &lhs; let rhs_value: &Value = &rhs; match Value::debug_eq(lhs_value, rhs_value) { @@ -258,8 +224,8 @@ define_type_features! { } } - [context] fn reinterpret_as_run(Spanned(this, span_range): Spanned>) -> ExecutionResult { - let source = this.into_inner().value.into_token_stream(); + [context] fn reinterpret_as_run(Spanned(this, span_range): Spanned>) -> ExecutionResult { + let source = this.into_inner().into_token_stream(); let (reparsed, scope_definitions) = source.source_parse_and_analyze(ExpressionBlockContent::parse, ExpressionBlockContent::control_flow_pass)?; let mut inner_interpreter = Interpreter::new(scope_definitions); let return_value = reparsed.evaluate_spanned(&mut inner_interpreter, span_range, RequestedOwnership::owned())?.expect_owned(); @@ -269,8 +235,8 @@ define_type_features! { Ok(return_value.0) } - fn reinterpret_as_stream(Spanned(this, span_range): Spanned>) -> ExecutionResult { - let source = this.into_inner().value.into_token_stream(); + fn reinterpret_as_stream(Spanned(this, span_range): Spanned>) -> ExecutionResult { + let source = this.into_inner().into_token_stream(); let (reparsed, scope_definitions) = source.source_parse_and_analyze( |input| SourceStream::parse_with_span(input, span_range.span_from_join_else_start()), SourceStream::control_flow_pass, @@ -281,9 +247,9 @@ define_type_features! { } } pub(crate) mod unary_operations { - [context] fn cast_coerced_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { + [context] fn cast_coerced_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { let this = this.into_inner(); - let coerced = this.value.coerce_into_value(); + let coerced = this.coerce_into_value(); if let Value::Stream(_) = &coerced { return span.value_err("The stream could not be coerced into a single value"); } @@ -552,8 +518,8 @@ impl Interpret for ConcatenatedStreamLiteral { fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { let stream = interpreter.capture_output(|interpreter| self.content.interpret(interpreter))?; - let string = stream.concat_recursive(&ConcatBehaviour::standard(self.span_range())); - let ident_span = StreamValue { value: stream } + let string = stream.concat_content(&ConcatBehaviour::standard(self.span_range())); + let ident_span = stream .resolve_content_span_range() .unwrap_or_else(|| self.span_range()) .join_into_span_else_start(); diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index 464b161b..1d5b050f 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -21,44 +21,9 @@ define_leaf_type! { }, } -#[derive(Clone)] -pub(crate) struct StringValue { - pub(crate) value: String, -} - -impl Debug for StringValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.value) - } -} - -impl IntoValue for StringValue { - fn into_value(self) -> Value { - Value::String(self) - } -} - -impl StringValue { - pub(super) fn for_litstr(lit: &syn::LitStr) -> Owned { - Self { value: lit.value() }.into_owned() - } - - pub(super) fn to_literal(&self, span: Span) -> Literal { - Literal::string(&self.value).with_span(span) - } -} - -impl HasLeafKind for StringValue { - type LeafKind = StringKind; - - fn kind(&self) -> Self::LeafKind { - StringKind - } -} - -impl ValuesEqual for StringValue { +impl ValuesEqual for String { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.value == other.value { + if self == other { ctx.values_equal() } else { ctx.leaf_values_not_equal(self, other) @@ -66,17 +31,9 @@ impl ValuesEqual for StringValue { } } -impl IntoValue for String { - fn into_value(self) -> Value { - Value::String(StringValue { value: self }) - } -} - impl IntoValue for &str { fn into_value(self) -> Value { - Value::String(StringValue { - value: self.to_string(), - }) + self.to_string().into_value() } } @@ -104,7 +61,6 @@ pub(crate) fn string_to_literal( define_type_features! { impl StringType, - parent: IterableType, pub(crate) mod string_interface { pub(crate) mod methods { // ================== @@ -251,18 +207,14 @@ define_type_features! { impl_resolvable_argument_for! { StringType, - (value, context) -> StringValue { + (value, context) -> String { match value { - Value::String(value) => Ok(value), + ValueContent::String(value) => Ok(value), _ => context.err("a string", value), } } } -impl_delegated_resolvable_argument_for!( - (value: StringValue) -> String { value.value } -); - impl ResolvableArgumentTarget for str { type ValueType = StringType; } @@ -273,7 +225,7 @@ impl ResolvableShared for str { context: ResolutionContext, ) -> ExecutionResult<&'a Self> { match value { - Value::String(s) => Ok(s.value.as_str()), + ValueContent::String(s) => Ok(s.as_str()), _ => context.err("a string", value), } } diff --git a/src/expressions/values/unsupported_literal.rs b/src/expressions/values/unsupported_literal.rs index c3f779cd..5e498cb1 100644 --- a/src/expressions/values/unsupported_literal.rs +++ b/src/expressions/values/unsupported_literal.rs @@ -2,7 +2,7 @@ use super::*; define_leaf_type! { pub(crate) UnsupportedLiteralType => ValueType(ValueContent::UnsupportedLiteral), - content: syn::Lit, + content: UnsupportedLiteral, kind: pub(crate) UnsupportedLiteralKind, type_name: "unsupported_literal", articled_display_name: "an unsupported literal", @@ -10,13 +10,11 @@ define_leaf_type! { } #[derive(Clone)] -pub(crate) struct UnsupportedLiteral { - pub(crate) lit: syn::Lit, -} +pub(crate) struct UnsupportedLiteral(pub(crate) syn::Lit); impl ValuesEqual for UnsupportedLiteral { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.lit.to_token_stream().to_string() == other.lit.to_token_stream().to_string() { + if self.0.to_token_stream().to_string() == other.0.to_token_stream().to_string() { ctx.values_equal() } else { ctx.leaf_values_not_equal(self, other) @@ -26,21 +24,12 @@ impl ValuesEqual for UnsupportedLiteral { impl Debug for UnsupportedLiteral { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.lit.to_token_stream()) - } -} - -impl HasLeafKind for UnsupportedLiteral { - type LeafKind = UnsupportedLiteralKind; - - fn kind(&self) -> Self::LeafKind { - UnsupportedLiteralKind + write!(f, "{}", self.0.to_token_stream()) } } define_type_features! { impl UnsupportedLiteralType, - parent: ValueType, pub(crate) mod unsupported_literal_interface { pub(crate) mod methods {} pub(crate) mod unary_operations {} diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index a36f7c14..052fafdf 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -1,10 +1,9 @@ use super::*; // TODO[concepts]: Uncomment when ready -// pub(crate) type QqqValue = Actual<'static, ValueType, BeOwned>; -// pub(crate) type QqqValueReferencable = Actual<'static, ValueType, BeReferenceable>; -// pub(crate) type QqqValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; -// pub(crate) type QqqValueMut<'a> = Actual<'a, ValueType, BeAnyMut>; +// pub(crate) type OwnedValue = QqqOwned; +// pub(crate) type ValueRef<'a> = QqqRef<'a, ValueType>; +// pub(crate) type ValueMut<'a> = QqqMut<'a, ValueType>; define_parent_type! { pub(crate) ValueType, @@ -32,28 +31,10 @@ define_parent_type! { articled_display_name: "any value", } -#[derive(Clone)] -pub(crate) enum Value { - None, - Integer(IntegerValue), - Float(FloatValue), - Boolean(BooleanValue), - String(StringValue), - Char(CharValue), - // Unsupported literal is a type here so that we can parse such a token - // as a value rather than a stream, and give it better error messages - UnsupportedLiteral(UnsupportedLiteral), - Array(ArrayValue), - Object(ObjectValue), - Stream(StreamValue), - Range(RangeValue), - Iterator(IteratorValue), - Parser(ParserValue), -} +pub(crate) type Value = ValueContent<'static, BeOwned>; define_type_features! { impl ValueType, - parent: ValueType, pub(crate) mod value_interface { pub(crate) mod methods { fn clone(this: CopyOnWriteValue) -> OwnedValue { @@ -115,7 +96,7 @@ define_type_features! { input.concat_recursive(&ConcatBehaviour::standard(span_range)) } - [context] fn with_span(value: Spanned, spans: AnyRef) -> ExecutionResult { + [context] fn with_span(value: Spanned, spans: AnyRef) -> ExecutionResult { let mut this = to_stream(context, value)?; let span_to_use = match spans.resolve_content_span_range() { Some(span_range) => span_range.span_from_join_else_start(), @@ -250,14 +231,14 @@ impl Value { Ok(float) => Some(float.into_owned_value()), Err(_) => None, }, - Lit::Bool(lit) => Some(BooleanValue::for_litbool(lit).into_owned_value()), - Lit::Str(lit) => Some(StringValue::for_litstr(lit).into_owned_value()), - Lit::Char(lit) => Some(CharValue::for_litchar(lit).into_owned_value()), + Lit::Bool(lit) => Some(lit.value.into_owned_value()), + Lit::Str(lit) => Some(lit.value().into_owned_value()), + Lit::Char(lit) => Some(lit.value().into_owned_value()), _ => None, }; match matched { Some(value) => value, - None => Self::UnsupportedLiteral(UnsupportedLiteral { lit }).into_owned(), + None => UnsupportedLiteral(lit).into_owned_value(), } } @@ -275,7 +256,7 @@ impl Value { } pub(crate) fn is_none(&self) -> bool { - matches!(self, Value::None) + matches!(&self, ValueContent::None(_)) } /// Recursively compares two values for equality using `ValuesEqual` semantics. @@ -289,32 +270,34 @@ impl ValuesEqual for Value { // Each variant has two lines: same-type comparison, then type-mismatch fallback. // This ensures adding a new variant only requires adding two lines at the bottom. match (self, other) { - (Value::None, Value::None) => ctx.values_equal(), - (Value::None, _) => ctx.kind_mismatch(self, other), - (Value::Boolean(l), Value::Boolean(r)) => l.test_equality(r, ctx), - (Value::Boolean(_), _) => ctx.kind_mismatch(self, other), - (Value::Char(l), Value::Char(r)) => l.test_equality(r, ctx), - (Value::Char(_), _) => ctx.kind_mismatch(self, other), - (Value::String(l), Value::String(r)) => l.test_equality(r, ctx), - (Value::String(_), _) => ctx.kind_mismatch(self, other), - (Value::Integer(l), Value::Integer(r)) => l.test_equality(r, ctx), - (Value::Integer(_), _) => ctx.kind_mismatch(self, other), - (Value::Float(l), Value::Float(r)) => l.test_equality(r, ctx), - (Value::Float(_), _) => ctx.kind_mismatch(self, other), - (Value::Array(l), Value::Array(r)) => l.test_equality(r, ctx), - (Value::Array(_), _) => ctx.kind_mismatch(self, other), - (Value::Object(l), Value::Object(r)) => l.test_equality(r, ctx), - (Value::Object(_), _) => ctx.kind_mismatch(self, other), - (Value::Stream(l), Value::Stream(r)) => l.test_equality(r, ctx), - (Value::Stream(_), _) => ctx.kind_mismatch(self, other), - (Value::Range(l), Value::Range(r)) => l.test_equality(r, ctx), - (Value::Range(_), _) => ctx.kind_mismatch(self, other), - (Value::UnsupportedLiteral(l), Value::UnsupportedLiteral(r)) => l.test_equality(r, ctx), - (Value::UnsupportedLiteral(_), _) => ctx.kind_mismatch(self, other), - (Value::Parser(l), Value::Parser(r)) => l.test_equality(r, ctx), - (Value::Parser(_), _) => ctx.kind_mismatch(self, other), - (Value::Iterator(l), Value::Iterator(r)) => l.test_equality(r, ctx), - (Value::Iterator(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::None(_), ValueContent::None(_)) => ctx.values_equal(), + (ValueContent::None(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Bool(l), ValueContent::Bool(r)) => l.test_equality(r, ctx), + (ValueContent::Bool(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Char(l), ValueContent::Char(r)) => l.test_equality(r, ctx), + (ValueContent::Char(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::String(l), ValueContent::String(r)) => l.test_equality(r, ctx), + (ValueContent::String(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Integer(l), ValueContent::Integer(r)) => l.test_equality(r, ctx), + (ValueContent::Integer(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Float(l), ValueContent::Float(r)) => l.test_equality(r, ctx), + (ValueContent::Float(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Array(l), ValueContent::Array(r)) => l.test_equality(r, ctx), + (ValueContent::Array(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Object(l), ValueContent::Object(r)) => l.test_equality(r, ctx), + (ValueContent::Object(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Stream(l), ValueContent::Stream(r)) => l.test_equality(r, ctx), + (ValueContent::Stream(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Range(l), ValueContent::Range(r)) => l.test_equality(r, ctx), + (ValueContent::Range(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::UnsupportedLiteral(l), ValueContent::UnsupportedLiteral(r)) => { + l.test_equality(r, ctx) + } + (ValueContent::UnsupportedLiteral(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Parser(l), ValueContent::Parser(r)) => l.test_equality(r, ctx), + (ValueContent::Parser(_), _) => ctx.kind_mismatch(self, other), + (ValueContent::Iterator(l), ValueContent::Iterator(r)) => l.test_equality(r, ctx), + (ValueContent::Iterator(_), _) => ctx.kind_mismatch(self, other), } } } @@ -326,8 +309,8 @@ impl Value { index: Spanned<&Self>, ) -> ExecutionResult { match self { - Value::Array(array) => array.into_indexed(index), - Value::Object(object) => object.into_indexed(index), + ValueContent::Array(array) => array.into_indexed(index), + ValueContent::Object(object) => object.into_indexed(index), other => access.type_err(format!("Cannot index into {}", other.articled_kind())), } } @@ -339,8 +322,8 @@ impl Value { auto_create: bool, ) -> ExecutionResult<&mut Self> { match self { - Value::Array(array) => array.index_mut(index), - Value::Object(object) => object.index_mut(index, auto_create), + ValueContent::Array(array) => array.index_mut(index), + ValueContent::Object(object) => object.index_mut(index, auto_create), other => access.type_err(format!("Cannot index into {}", other.articled_kind())), } } @@ -351,15 +334,15 @@ impl Value { index: Spanned<&Self>, ) -> ExecutionResult<&Self> { match self { - Value::Array(array) => array.index_ref(index), - Value::Object(object) => object.index_ref(index), + ValueContent::Array(array) => array.index_ref(index), + ValueContent::Object(object) => object.index_ref(index), other => access.type_err(format!("Cannot index into {}", other.articled_kind())), } } pub(crate) fn into_property(self, access: &PropertyAccess) -> ExecutionResult { match self { - Value::Object(object) => object.into_property(access), + ValueContent::Object(object) => object.into_property(access), other => access.type_err(format!( "Cannot access properties on {}", other.articled_kind() @@ -373,7 +356,7 @@ impl Value { auto_create: bool, ) -> ExecutionResult<&mut Self> { match self { - Value::Object(object) => object.property_mut(access, auto_create), + ValueContent::Object(object) => object.property_mut(access, auto_create), other => access.type_err(format!( "Cannot access properties on {}", other.articled_kind() @@ -383,7 +366,7 @@ impl Value { pub(crate) fn property_ref(&self, access: &PropertyAccess) -> ExecutionResult<&Self> { match self { - Value::Object(object) => object.property_ref(access), + ValueContent::Object(object) => object.property_ref(access), other => access.type_err(format!( "Cannot access properties on {}", other.articled_kind() @@ -397,11 +380,11 @@ impl Value { error_span_range: SpanRange, ) -> ExecutionResult { match (self, grouping) { - (Self::Stream(value), Grouping::Flattened) => Ok(value.value), - (Self::Stream(value), Grouping::Grouped) => { - let mut output = OutputStream::new(); + (ValueContent::Stream(value), Grouping::Flattened) => Ok(value), + (ValueContent::Stream(value), Grouping::Grouped) => { + let mut output: OutputStream = OutputStream::new(); let span = ToStreamContext::new(&mut output, error_span_range).new_token_span(); - output.push_new_group(value.value, Delimiter::None, span); + output.push_new_group(value, Delimiter::None, span); Ok(output) } (other, grouping) => other.output_to_new_stream(grouping, error_span_range), @@ -443,42 +426,42 @@ impl Value { fn output_flattened_to(&self, output: &mut ToStreamContext) -> ExecutionResult<()> { match self { - Self::None => {} - Self::Integer(value) => { + ValueContent::None(_) => {} + ValueContent::Integer(value) => { let literal = value.to_literal(output.new_token_span()); output.push_literal(literal); } - Self::Float(value) => { + ValueContent::Float(value) => { value.output_to(output); } - Self::Boolean(value) => { - let ident = value.to_ident(output.new_token_span()); + ValueContent::Bool(value) => { + let ident = Ident::new_bool(*value, output.new_token_span()); output.push_ident(ident); } - Self::String(value) => { - let literal = value.to_literal(output.new_token_span()); + ValueContent::String(value) => { + let literal = Literal::string(value).with_span(output.new_token_span()); output.push_literal(literal); } - Self::Char(value) => { - let literal = value.to_literal(output.new_token_span()); + ValueContent::Char(value) => { + let literal = Literal::character(*value).with_span(output.new_token_span()); output.push_literal(literal); } - Self::UnsupportedLiteral(literal) => { - output.extend_raw_tokens(literal.lit.to_token_stream()) + ValueContent::UnsupportedLiteral(literal) => { + output.extend_raw_tokens(literal.0.to_token_stream()) } - Self::Object(_) => { + ValueContent::Object(_) => { return output.type_err("Objects cannot be output to a stream"); } - Self::Array(array) => array.output_items_to(output, Grouping::Flattened)?, - Self::Stream(value) => value.value.append_cloned_into(output.output_stream), - Self::Iterator(iterator) => iterator + ValueContent::Array(array) => array.output_items_to(output, Grouping::Flattened)?, + ValueContent::Stream(value) => value.append_cloned_into(output.output_stream), + ValueContent::Iterator(iterator) => iterator .clone() .output_items_to(output, Grouping::Flattened)?, - Self::Range(range) => { - let iterator = IteratorValue::new_for_range(range.clone())?; + ValueContent::Range(value) => { + let iterator = IteratorValue::new_for_range(value.clone())?; iterator.output_items_to(output, Grouping::Flattened)? } - Self::Parser(_) => { + ValueContent::Parser(_) => { return output.type_err("Parsers cannot be output to a stream"); } }; @@ -497,42 +480,42 @@ impl Value { behaviour: &ConcatBehaviour, ) -> ExecutionResult<()> { match self { - Value::None => { + ValueContent::None(_) => { if behaviour.show_none_values { output.push_str("None"); } } - Value::Stream(stream) => { - stream.concat_recursive_into(output, behaviour); + ValueContent::Stream(stream) => { + stream.concat_as_literal_into(output, behaviour); } - Value::Array(array) => { + ValueContent::Array(array) => { array.concat_recursive_into(output, behaviour)?; } - Value::Object(object) => { + ValueContent::Object(object) => { object.concat_recursive_into(output, behaviour)?; } - Value::Iterator(iterator) => { + ValueContent::Iterator(iterator) => { iterator.concat_recursive_into(output, behaviour)?; } - Value::Range(range) => { + ValueContent::Range(range) => { range.concat_recursive_into(output, behaviour)?; } - Value::Parser(_) => { + ValueContent::Parser(_) => { return behaviour .error_span_range .type_err("Parsers cannot be output to a string"); } - Value::Integer(_) - | Value::Float(_) - | Value::Char(_) - | Value::Boolean(_) - | Value::UnsupportedLiteral(_) - | Value::String(_) => { + ValueContent::Integer(_) + | ValueContent::Float(_) + | ValueContent::Char(_) + | ValueContent::Bool(_) + | ValueContent::UnsupportedLiteral(_) + | ValueContent::String(_) => { // This isn't the most efficient, but it's less code and debug doesn't need to be super efficient. let stream = self .output_to_new_stream(Grouping::Flattened, behaviour.error_span_range) .expect("Non-composite values should all be able to be outputted to a stream"); - stream.concat_recursive_into(output, behaviour); + stream.concat_content_into(output, behaviour); } } Ok(()) @@ -605,39 +588,8 @@ impl Spanned { } } -impl IntoValue for Value { - fn into_value(self) -> Value { - self - } -} - #[derive(Copy, Clone)] pub(crate) enum Grouping { Grouped, Flattened, } - -// TODO[concepts]: Remove when this is auto-generated after Value changes -impl HasLeafKind for Value { - type LeafKind = ValueLeafKind; - - fn kind(&self) -> ValueLeafKind { - match self { - Value::None => ValueLeafKind::None(NoneKind), - Value::Integer(integer) => ValueLeafKind::Integer(integer.kind()), - Value::Float(float) => ValueLeafKind::Float(float.kind()), - Value::Boolean(_) => ValueLeafKind::Bool(BoolKind), - Value::String(_) => ValueLeafKind::String(StringKind), - Value::Char(_) => ValueLeafKind::Char(CharKind), - Value::Array(_) => ValueLeafKind::Array(ArrayKind), - Value::Object(_) => ValueLeafKind::Object(ObjectKind), - Value::Stream(_) => ValueLeafKind::Stream(StreamKind), - Value::Range(_) => ValueLeafKind::Range(RangeKind), - Value::Iterator(_) => ValueLeafKind::Iterator(IteratorKind), - Value::Parser(_) => ValueLeafKind::Parser(ParserKind), - Value::UnsupportedLiteral(_) => { - ValueLeafKind::UnsupportedLiteral(UnsupportedLiteralKind) - } - } - } -} diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index a0fe5a2a..168d81c7 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -286,7 +286,7 @@ impl Spanned { pub(crate) fn into_statement_result(self) -> ExecutionResult<()> { let Spanned(value, span_range) = self; match value.0 { - Value::None => Ok(()), + ValueContent::None(_) => Ok(()), _ => span_range.control_flow_err("A non-returning statement must not return a value. If you wish to explicitly discard the expression's result, use `let _ = ...;`. Alternatively, If you wish to output the value into the parent token stream, use `emit ...;`"), } } diff --git a/src/interpretation/output_stream.rs b/src/interpretation/output_stream.rs index 6cbb7a07..0072a0e9 100644 --- a/src/interpretation/output_stream.rs +++ b/src/interpretation/output_stream.rs @@ -234,13 +234,13 @@ impl OutputStream { } } - pub(crate) fn concat_recursive(&self, behaviour: &ConcatBehaviour) -> String { + pub(crate) fn concat_content(&self, behaviour: &ConcatBehaviour) -> String { let mut output = String::new(); - self.concat_recursive_into(&mut output, behaviour); + self.concat_content_into(&mut output, behaviour); output } - pub(crate) fn concat_recursive_into(&self, output: &mut String, behaviour: &ConcatBehaviour) { + pub(crate) fn concat_content_into(&self, output: &mut String, behaviour: &ConcatBehaviour) { fn concat_recursive_interpreted_stream( behaviour: &ConcatBehaviour, output: &mut String, From 25cdccaa0b0b2623e3144e7a6c52dbad4a5f4b09 Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 5 Jan 2026 02:36:52 +0100 Subject: [PATCH 19/58] fix: Fix MSRV compilation --- src/expressions/concepts/forms/simple_ref.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index ec52dde6..7e5bdd32 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -44,6 +44,7 @@ where pub(crate) fn as_ref<'r>(&'r self) -> Actual<'r, T, BeRef> { match self.map_ref_with::() { Ok(x) => x, + Err(infallible) => match infallible {}, // Need to include because of MSRV } } } From 7426af5b3b4da29aa1a37de10aac3b30c1af44f1 Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 5 Jan 2026 02:39:23 +0100 Subject: [PATCH 20/58] fix: Fully fix MSRV compilation --- src/expressions/concepts/forms/simple_mut.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index 345cd101..1cb63d61 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -52,6 +52,7 @@ where pub(crate) fn as_mut<'r>(&'r mut self) -> Actual<'r, T, BeMut> { match self.map_mut_with::() { Ok(x) => x, + Err(infallible) => match infallible {}, // Need to include because of MSRV } } } From 1cd07c1be431759ddcc173c0e3258a54aa24c30a Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 6 Jan 2026 00:13:58 +0100 Subject: [PATCH 21/58] feat: Add compile time benchmarks --- .github/workflows/ci.yml | 1 + bench.sh | 21 +++++++++++++++--- bench_compilation.sh | 48 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100755 bench_compilation.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca5bc723..7740c6a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: stable + - run: ./bench_compilation.sh - run: ./bench.sh book: diff --git a/bench.sh b/bench.sh index 821f391a..59c81928 100755 --- a/bench.sh +++ b/bench.sh @@ -12,8 +12,23 @@ cd "$(dirname "$0")" # - Overhead invoking the macro # - The measured Parsing + Evaluation + Output time in the benchmark # - So the benchmark is useful, but should be considered alongside the compile time -# of preinterpret itself... +# of preinterpret itself - see `bench_compilation.sh` for that. -cargo bench --features benchmark --profile=dev; +echo Preparing benchmark builds... +cargo build --bench basic --features benchmark --profile=dev +cargo build --bench basic --features benchmark --profile=release -cargo bench --features benchmark; \ No newline at end of file +# Note - the benchmarks themselves actually run *during* build time +# So at this point (courtesy of the build cache) we already have the benchmark results. +# But we print them in a block to make it easier to copy-paste them + +echo +echo "Executing pre-run benchmark (dev profile)..." +cargo bench --bench basic --features benchmark --profile=dev + +echo +echo "Executing pre-run benchmark (release profile)..." +cargo bench --bench basic --features benchmark --profile=release + +echo +echo "If you want to get fresh results, run 'cargo clean' and re-run this script" \ No newline at end of file diff --git a/bench_compilation.sh b/bench_compilation.sh new file mode 100755 index 00000000..bdef9cd4 --- /dev/null +++ b/bench_compilation.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +set -e + +cd "$(dirname "$0")" + +# Usage: run_build_with_timings +run_build_with_timings() { + local profile="$1" + + echo + echo ">> Checking compilation timings ($profile profile)" + echo + cargo clean + + cargo build --lib --profile="$profile" --timings > /dev/null + + # Find the most recent timing file (cargo creates timestamped files) + local timing_file + timing_file=$(ls -t target/cargo-timings/cargo-timing-*.html 2>/dev/null | head -1) || true + + if [ -n "$timing_file" ] && [ -f "$timing_file" ]; then + echo + + # Extract total time from the HTML summary table + local total_time + total_time=$(grep -o 'Total time:[^<]*' "$timing_file" | grep -o '[0-9.]*s' | head -1) || true + echo "- Total lib build time | $total_time" + + # Extract preinterpret duration from UNIT_DATA JSON + # Use sed to extract just the duration value after finding the preinterpret entry + local preinterpret_duration + preinterpret_duration=$(sed -n '/"name": "preinterpret"/,/}/p' "$timing_file" | grep -o '"duration": [0-9.]*' | grep -o '[0-9.]*') || true + if [ -n "$preinterpret_duration" ]; then + echo "- preinterpret compile time | ${preinterpret_duration}s" + fi + + # Extract syn duration from UNIT_DATA JSON + local syn_duration + syn_duration=$(sed -n '/"name": "syn"/,/}/p' "$timing_file" | grep -o '"duration": [0-9.]*' | grep -o '[0-9.]*') || true + if [ -n "$syn_duration" ]; then + echo "- syn compile time | ${syn_duration}s" + fi + fi +} + +run_build_with_timings "dev" +run_build_with_timings "release" From c80da3740fbddfcc9bfba389145034585be12b4b Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 6 Jan 2026 00:14:29 +0100 Subject: [PATCH 22/58] feat: Partial completion of float migration --- plans/TODO.md | 13 + src/expressions/concepts/actual.rs | 53 ++-- src/expressions/concepts/form.rs | 34 ++- src/expressions/concepts/forms/owned.rs | 7 +- src/expressions/concepts/type_traits.rs | 18 +- src/expressions/patterns.rs | 4 +- src/expressions/values/float.rs | 318 ++++++++++++----------- src/expressions/values/float_subtypes.rs | 12 +- src/expressions/values/float_untyped.rs | 32 +-- src/expressions/values/parser.rs | 4 +- src/expressions/values/value.rs | 2 +- tests/operations.rs | 2 + 12 files changed, 283 insertions(+), 216 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index dfc439cb..c67f639b 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -232,7 +232,20 @@ First, read the @./2025-11-vision.md - [x] Replace `Value` with `type Value = ValueContent<'static, BeOwned>` - [x] Get rid of the `Actual` wrapper inside content - [ ] --- + - [ ] Improve mappers: + - [ ] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` + - [ ] 6 methods... `map_content`, `map_content_ref`, `map_content_mut` + - [ ] ... and a `ReduceMapper::::map(content, |x| -> y)` + - [ ] ... Probably remove e.g. `map_with` etc on actual? + - [ ] Trial getting rid of `Actual` completely? + * Maybe it's just a type alias? + * Downcast / Upcast become traits + * It might cause issues for e.g. - [ ] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` + - [ ] Create new branch + - [ ] Resolve issue with `2.3` not resolving into `2f32` any more + - [ ] Possibly need an explicit `MaybeTyped<..>` which does not implement `FromValueContent` but + rather has e.g. `MaybeTyped` implement directly `IsArgument` and `ResolveAs> for FloatContent` (if it doesn't conflict?) - [ ] .. same for int... - [ ] Change `type Value<'a, F> = ValueContent<'a, F>` and `type OwnedValue` with: - [ ] `type OwnedValue = QqqOwned` diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs index 92e5902f..7723d95a 100644 --- a/src/expressions/concepts/actual.rs +++ b/src/expressions/concepts/actual.rs @@ -17,7 +17,14 @@ impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> { { Actual(T::upcast_to(self.0)) } +} +// Hierarchical implementations +impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> + where + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, +{ #[inline] pub(crate) fn downcast>(self) -> Option> where @@ -29,40 +36,31 @@ impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> { #[inline] pub(crate) fn map_with>( self, - ) -> Result, M::ShortCircuit<'a>> - where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, - { + ) -> Result, M::ShortCircuit<'a>> { T::map_with::<'a, F, M>(self.0).map(|c| Actual(c)) } #[inline] pub(crate) fn map_ref_with<'r, M: RefLeafMapper>( &'r self, - ) -> Result, M::ShortCircuit<'a>> - where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, - { + ) -> Result, M::ShortCircuit<'a>> { T::map_ref_with::(&self.0).map(|c| Actual(c)) } #[inline] pub(crate) fn map_mut_with<'r, M: MutLeafMapper>( &'r mut self, - ) -> Result, M::ShortCircuit<'a>> - where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, - { + ) -> Result, M::ShortCircuit<'a>> { T::map_mut_with::(&mut self.0).map(|c| Actual(c)) } } -impl<'a, T: IsType, F: IsFormOf> Spanned> { - #[inline] - pub(crate) fn resolve_as>( +impl<'a, T: IsType, F: IsFormOf> Spanned> + where + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, +{ + pub(crate) fn resolve>( self, description: &str, ) -> ExecutionResult @@ -71,8 +69,23 @@ impl<'a, T: IsType, F: IsFormOf> Spanned> { >::Type: DowncastFrom, { let Spanned(value, span_range) = self; - let resolved = <>::Type>::resolve(value, span_range, description)?; - Ok(X::from_actual(resolved)) + let resolved = <>::Type>::resolve(value.0, span_range, description)?; + Ok(X::from_content(resolved)) + } +} + +// TODO[concepts]: Remove resolve_as eventually? +impl<'a, T: IsType, F: IsFormOf, X: FromValueContent<'a, Form = F>> ResolveAs for Spanned> + where + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, + F: IsFormOf<>::Type>, + >::Type: DowncastFrom, +{ + fn resolve_as(self, description: &str) -> ExecutionResult { + let Spanned(value, span_range) = self; + let resolved = <>::Type>::resolve(value.0, span_range, description)?; + Ok(X::from_content(resolved)) } } diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index a4867562..b0241db5 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -80,7 +80,7 @@ pub(crate) trait MapIntoReturned: IsFormOf { // impl< // X: FromValueContent<'static, Type = T, Form = F>, // F: IsForm + MapFromArgument, -// T: DowncastFrom, +// T: TypeData + DowncastFrom, // > IsArgument for X { // type ValueType = T; // const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; @@ -91,10 +91,28 @@ pub(crate) trait MapIntoReturned: IsFormOf { // } // } +// TODO[concepts]: Replace with the above impl when ready +impl< + F: IsFormOf + MapFromArgument, + T: TypeData + DowncastFrom, +> IsArgument for Actual<'static, T, F> +where + for<'l> ValueType: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, +{ + type ValueType = T; + const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; + fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + let ownership_mapped = F::from_argument_value(value)?; + let type_mapped = T::resolve(ownership_mapped.0, span_range, "This argument")?; + Ok(Actual(type_mapped)) + } +} + // Clashes with other blanket impl it will replace! // // impl< -// X: IntoValueContent<'static, TypeData = T, Ownership = F>, +// X: IntoValueContent<'static, Type = T, Form = F>, // F: IsForm + MapIntoReturned, // T: UpcastTo, // > IsReturnable for X { @@ -104,3 +122,15 @@ pub(crate) trait MapIntoReturned: IsFormOf { // F::into_returned_value(type_mapped) // } // } + +// TODO[concepts]: Replace with the above impl when ready +impl< + F: IsFormOf + MapIntoReturned, + T: UpcastTo, +> IsReturnable for Actual<'static, T, F> { + fn to_returned_value(self) -> ExecutionResult { + let type_mapped = self.into_actual() + .upcast::(); + F::into_returned_value(type_mapped) + } +} \ No newline at end of file diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index fa45f35d..c5eac5fd 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -47,10 +47,9 @@ impl MapFromArgument for BeOwned { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; fn from_argument_value( - _value: ArgumentValue, + value: ArgumentValue, ) -> ExecutionResult> { - // value.expect_owned() - todo!() + Ok(value.expect_owned().0.into_actual()) } } @@ -63,7 +62,7 @@ mod test { let owned_value: QqqOwned = QqqOwned::of(42u64); let resolved = owned_value .spanned(Span::call_site().span_range()) - .resolve_as::("My value") + .resolve::("My value") .unwrap(); assert_eq!(resolved, 42u64); } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index f4a23067..531de00e 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -84,28 +84,32 @@ pub(crate) trait UpcastTo + IsFormOf>: IsType { ) -> >::Content<'a>; } -pub(crate) trait DowncastFrom + IsFormOf>: IsType { +pub(crate) trait DowncastFrom + IsFormOf>: IsType +where + for<'l> T: IsHierarchicalType = >::Content<'l>>, +{ fn downcast_from<'a>( content: >::Content<'a>, ) -> Option<>::Content<'a>>; fn resolve<'a>( - actual: Actual<'a, T, F>, + content: >::Content<'a>, span_range: SpanRange, resolution_target: &str, - ) -> ExecutionResult> { - let content = match Self::downcast_from(actual.0) { + ) -> ExecutionResult<>::Content<'a>> { + let leaf_kind = T::content_to_leaf_kind::(&content); + let content = match Self::downcast_from(content) { Some(c) => c, None => { return span_range.value_err(format!( "{} is expected to be {}, but it is {}", resolution_target, Self::ARTICLED_DISPLAY_NAME, - T::ARTICLED_DISPLAY_NAME, + leaf_kind.articled_display_name(), )) } }; - Ok(Actual(content)) + Ok(content) } } @@ -168,7 +172,7 @@ macro_rules! impl_type_feature_resolver { pub(crate) use impl_type_feature_resolver; macro_rules! impl_ancestor_chain_conversions { - ($child:ty $(=> $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?) => { + ($child:ty $(=> $parent:ident ($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?) => { impl DowncastFrom<$child, F> for $child { fn downcast_from<'a>( diff --git a/src/expressions/patterns.rs b/src/expressions/patterns.rs index 2a471ad4..34b9774d 100644 --- a/src/expressions/patterns.rs +++ b/src/expressions/patterns.rs @@ -355,7 +355,7 @@ impl HandleDestructure for StreamPattern { value: Value, ) -> ExecutionResult<()> { let stream = Spanned(Actual::of(value), self.brackets.span_range()) - .resolve_as("The value destructured with a stream pattern")?; + .resolve("The value destructured with a stream pattern")?; interpreter.start_parse(stream, |interpreter, _| self.content.consume(interpreter)) } } @@ -397,7 +397,7 @@ impl HandleDestructure for ParseTemplatePattern { value: Value, ) -> ExecutionResult<()> { let stream = Spanned(Actual::of(value), self.brackets.span_range()) - .resolve_as("The value destructured with a parse template pattern")?; + .resolve("The value destructured with a parse template pattern")?; interpreter.start_parse(stream, |interpreter, handle| { self.parser_definition.define(interpreter, handle); self.content.consume(interpreter) diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index c25ee63f..f6a2b0ec 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -14,30 +14,55 @@ define_parent_type! { articled_display_name: "a float", } -pub(crate) type FloatValue = FloatContent<'static, BeOwned>; +pub(crate) type OwnedFloat = QqqOwned; +pub(crate) type OwnedFloatContent = FloatContent<'static, BeOwned>; +pub(crate) type FloatRef<'a> = QqqRef<'a, FloatType>; -impl FloatValue { - pub(super) fn for_litfloat(lit: &syn::LitFloat) -> ParseResult> { +impl OwnedFloat { + pub(super) fn for_litfloat(lit: &syn::LitFloat) -> ParseResult { Ok(match lit.suffix() { - "" => Self::Untyped(UntypedFloat::new_from_lit_float(lit)?), - "f32" => Self::F32(lit.base10_parse()?), - "f64" => Self::F64(lit.base10_parse()?), + "" => FloatContent::Untyped(UntypedFloat::new_from_lit_float(lit)?), + "f32" => FloatContent::F32(lit.base10_parse()?), + "f64" => FloatContent::F64(lit.base10_parse()?), suffix => { return lit.span().parse_err(format!( "The literal suffix {suffix} is not supported in preinterpret expressions" )); } } - .into_owned()) + .into_actual()) } + pub(crate) fn resolve_untyped_to_match(self, target: FloatRef) -> OwnedFloatContent { + match self.0 { + FloatContent::Untyped(this) => this.into_owned().into_kind(target.kind()), + other => other, + } + } + + #[allow(dead_code)] + pub(super) fn to_literal(self, span: Span) -> Literal { + self.to_unspanned_literal().with_span(span) + } + + fn to_unspanned_literal(self) -> Literal { + match self.0 { + FloatContent::Untyped(float) => float.to_unspanned_literal(), + FloatContent::F32(float) => Literal::f32_suffixed(float), + FloatContent::F64(float) => Literal::f64_suffixed(float), + } + } +} + +// TODO[concepts]: Move to FloatRef<'a> when we can +impl OwnedFloatContent { /// Outputs this float value to a token stream. /// For finite values, outputs a literal. For non-finite values (infinity, NaN), /// outputs the equivalent constant path like `f32::INFINITY`. pub(super) fn output_to(&self, output: &mut ToStreamContext) { let span = output.new_token_span(); match self { - FloatValue::Untyped(float) => { + FloatContent::Untyped(float) => { let f = float.into_fallback(); if f.is_finite() { output.push_literal(Literal::f64_unsuffixed(f).with_span(span)); @@ -50,7 +75,7 @@ impl FloatValue { output.extend_raw_tokens(quote::quote_spanned!(span=> f64::NEG_INFINITY)); } } - FloatValue::F32(f) => { + FloatContent::F32(f) => { if f.is_finite() { output.push_literal(Literal::f32_suffixed(*f).with_span(span)); } else if f.is_nan() { @@ -61,7 +86,7 @@ impl FloatValue { output.extend_raw_tokens(quote::quote_spanned!(span=> f32::NEG_INFINITY)); } } - FloatValue::F64(f) => { + FloatContent::F64(f) => { if f.is_finite() { output.push_literal(Literal::f64_suffixed(*f).with_span(span)); } else if f.is_nan() { @@ -74,86 +99,67 @@ impl FloatValue { } } } +} - #[allow(dead_code)] - pub(super) fn to_literal(self, span: Span) -> Literal { - self.to_unspanned_literal().with_span(span) - } - - pub(crate) fn resolve_untyped_to_match(this: Owned, target: &FloatValue) -> Self { - match this.into_inner() { - FloatValue::Untyped(this) => this.into_owned().into_kind(target.kind()), - other => other, - } - } - - pub(crate) fn assign_op( - mut left: Assignee, - right: R, - context: BinaryOperationCallContext, - op: fn(BinaryOperationCallContext, Owned, R) -> ExecutionResult, - ) -> ExecutionResult<()> { - let left_value = core::mem::replace(&mut *left, FloatValue::F32(0.0)); - let result = op(context, left_value.into_owned(), right)?; - *left = result; - Ok(()) - } - fn to_unspanned_literal(self) -> Literal { - match self { - FloatValue::Untyped(float) => float.to_unspanned_literal(), - FloatValue::F32(float) => Literal::f32_suffixed(float), - FloatValue::F64(float) => Literal::f64_suffixed(float), - } - } +fn assign_op( + mut left: Assignee, + right: R, + context: BinaryOperationCallContext, + op: fn(BinaryOperationCallContext, OwnedFloat, R) -> ExecutionResult, +) -> ExecutionResult<()> { + let left_value = core::mem::replace(&mut *left, FloatContent::F32(0.0)); + let result = op(context, left_value.into_actual(), right)?; + *left = result; + Ok(()) } -impl Debug for FloatValue { + +impl Debug for OwnedFloatContent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Untyped(v) => write!(f, "{}", v.into_fallback()), - Self::F32(v) => write!(f, "{:?}", v), - Self::F64(v) => write!(f, "{:?}", v), + FloatContent::Untyped(v) => write!(f, "{}", v.into_fallback()), + FloatContent::F32(v) => write!(f, "{:?}", v), + FloatContent::F64(v) => write!(f, "{:?}", v), } } } -impl FloatValue { - /// Aligns types for comparison - converts untyped to match the other's type. - /// Unlike integers, float conversion never fails (may lose precision). - fn align_types(mut lhs: Self, mut rhs: Self) -> (Self, Self) { - match (&lhs, &rhs) { - (FloatValue::Untyped(l), typed) if !matches!(typed, FloatValue::Untyped(_)) => { - lhs = l.into_kind(typed.kind()); - } - (typed, FloatValue::Untyped(r)) if !matches!(typed, FloatValue::Untyped(_)) => { - rhs = r.into_kind(lhs.kind()); - } - _ => {} // Both same type or both untyped - no conversion needed +/// Aligns types for comparison - converts untyped to match the other's type. +/// Unlike integers, float conversion never fails (may lose precision). +fn align_types(mut lhs: OwnedFloatContent, mut rhs: OwnedFloatContent) -> (OwnedFloatContent, OwnedFloatContent) { + match (&lhs, &rhs) { + (FloatContent::Untyped(l), typed) if !matches!(typed, FloatContent::Untyped(_)) => { + lhs = l.into_kind(typed.kind()); + } + (typed, FloatContent::Untyped(r)) if !matches!(typed, FloatContent::Untyped(_)) => { + rhs = r.into_kind(lhs.kind()); } - (lhs, rhs) + _ => {} // Both same type or both untyped - no conversion needed } + (lhs, rhs) } -impl ValuesEqual for FloatValue { +// TODO[concepts]: Should really be over FloatRef<'a> +impl ValuesEqual for OwnedFloatContent { /// Handles type coercion between typed and untyped floats. /// Uses Rust's float `==`, so `NaN != NaN`. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { // Align types (untyped -> typed conversion) - let (lhs, rhs) = Self::align_types(*self, *other); + let (lhs, rhs) = align_types(*self, *other); // After alignment, compare directly. // Each variant has two lines: same-type comparison, then type-mismatch fallback. // This ensures adding a new variant causes a compiler error. let equal = match (lhs, rhs) { - (FloatValue::Untyped(l), FloatValue::Untyped(r)) => { + (FloatContent::Untyped(l), FloatContent::Untyped(r)) => { l.into_fallback() == r.into_fallback() } - (FloatValue::Untyped(_), _) => return ctx.leaf_values_not_equal(self, other), - (FloatValue::F32(l), FloatValue::F32(r)) => l == r, - (FloatValue::F32(_), _) => return ctx.leaf_values_not_equal(self, other), - (FloatValue::F64(l), FloatValue::F64(r)) => l == r, - (FloatValue::F64(_), _) => return ctx.leaf_values_not_equal(self, other), + (FloatContent::Untyped(_), _) => return ctx.leaf_values_not_equal(self, other), + (FloatContent::F32(l), FloatContent::F32(r)) => l == r, + (FloatContent::F32(_), _) => return ctx.leaf_values_not_equal(self, other), + (FloatContent::F64(l), FloatContent::F64(r)) => l == r, + (FloatContent::F64(_), _) => return ctx.leaf_values_not_equal(self, other), }; if equal { @@ -168,154 +174,154 @@ define_type_features! { impl FloatType, pub(crate) mod float_interface { pub(crate) mod methods { - fn is_nan(this: FloatValue) -> bool { - match this { - FloatValue::Untyped(x) => x.into_fallback().is_nan(), - FloatValue::F32(x) => x.is_nan(), - FloatValue::F64(x) => x.is_nan(), + fn is_nan(this: OwnedFloat) -> bool { + match this.0 { + FloatContent::Untyped(x) => x.into_fallback().is_nan(), + FloatContent::F32(x) => x.is_nan(), + FloatContent::F64(x) => x.is_nan(), } } - fn is_infinite(this: FloatValue) -> bool { - match this { - FloatValue::Untyped(x) => x.into_fallback().is_infinite(), - FloatValue::F32(x) => x.is_infinite(), - FloatValue::F64(x) => x.is_infinite(), + fn is_infinite(this: OwnedFloat) -> bool { + match this.0 { + FloatContent::Untyped(x) => x.into_fallback().is_infinite(), + FloatContent::F32(x) => x.is_infinite(), + FloatContent::F64(x) => x.is_infinite(), } } - fn is_finite(this: FloatValue) -> bool { - match this { - FloatValue::Untyped(x) => x.into_fallback().is_finite(), - FloatValue::F32(x) => x.is_finite(), - FloatValue::F64(x) => x.is_finite(), + fn is_finite(this: OwnedFloat) -> bool { + match this.0 { + FloatContent::Untyped(x) => x.into_fallback().is_finite(), + FloatContent::F32(x) => x.is_finite(), + FloatContent::F64(x) => x.is_finite(), } } - fn is_sign_positive(this: FloatValue) -> bool { - match this { - FloatValue::Untyped(x) => x.into_fallback().is_sign_positive(), - FloatValue::F32(x) => x.is_sign_positive(), - FloatValue::F64(x) => x.is_sign_positive(), + fn is_sign_positive(this: OwnedFloat) -> bool { + match this.0 { + FloatContent::Untyped(x) => x.into_fallback().is_sign_positive(), + FloatContent::F32(x) => x.is_sign_positive(), + FloatContent::F64(x) => x.is_sign_positive(), } } - fn is_sign_negative(this: FloatValue) -> bool { - match this { - FloatValue::Untyped(x) => x.into_fallback().is_sign_negative(), - FloatValue::F32(x) => x.is_sign_negative(), - FloatValue::F64(x) => x.is_sign_negative(), + fn is_sign_negative(this: OwnedFloat) -> bool { + match this.0 { + FloatContent::Untyped(x) => x.into_fallback().is_sign_negative(), + FloatContent::F32(x) => x.is_sign_negative(), + FloatContent::F64(x) => x.is_sign_negative(), } } } pub(crate) mod unary_operations { } pub(crate) mod binary_operations { - fn add(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a + b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a + b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a + b), + fn add(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a + b), + FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a + b), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a + b), } } - [context] fn add_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, add) + [context] fn add_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, add) } - fn sub(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a - b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a - b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a - b), + fn sub(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a - b), + FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a - b), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a - b), } } - [context] fn sub_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, sub) + [context] fn sub_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, sub) } - fn mul(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a * b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a * b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a * b), + fn mul(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a * b), + FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a * b), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a * b), } } - [context] fn mul_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, mul) + [context] fn mul_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, mul) } - fn div(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a / b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a / b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a / b), + fn div(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a / b), + FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a / b), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a / b), } } - [context] fn div_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, div) + [context] fn div_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, div) } - fn rem(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a % b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a % b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a % b), + fn rem(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a % b), + FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a % b), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a % b), } } - [context] fn rem_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, rem) + [context] fn rem_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, rem) } - fn lt(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a < b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a < b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a < b), + fn lt(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a < b), + FloatContent::F32(left) => left.paired_comparison(right, |a, b| a < b), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a < b), } } - fn le(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a <= b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a <= b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a <= b), + fn le(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a <= b), + FloatContent::F32(left) => left.paired_comparison(right, |a, b| a <= b), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a <= b), } } - fn gt(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a > b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a > b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a > b), + fn gt(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a > b), + FloatContent::F32(left) => left.paired_comparison(right, |a, b| a > b), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a > b), } } - fn ge(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a >= b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a >= b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a >= b), + fn ge(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a >= b), + FloatContent::F32(left) => left.paired_comparison(right, |a, b| a >= b), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a >= b), } } - fn eq(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a == b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a == b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a == b), + fn eq(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a == b), + FloatContent::F32(left) => left.paired_comparison(right, |a, b| a == b), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a == b), } } - fn ne(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a != b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a != b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a != b), + fn ne(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref()) { + FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a != b), + FloatContent::F32(left) => left.paired_comparison(right, |a, b| a != b), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a != b), } } } @@ -352,7 +358,7 @@ define_type_features! { impl_resolvable_argument_for! { FloatType, - (value, context) -> FloatValue { + (value, context) -> OwnedFloatContent { match value { Value::Float(value) => Ok(value), other => context.err("a float", other), diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 6844fb5c..7847419d 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -162,20 +162,20 @@ macro_rules! impl_resolvable_float_subtype { type ValueType = $type_def; } - impl From<$type> for FloatValue { + impl From<$type> for OwnedFloatContent { fn from(value: $type) -> Self { - FloatValue::$variant(value) + FloatContent::$variant(value) } } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - value: FloatValue, + value: OwnedFloatContent, context: ResolutionContext, ) -> ExecutionResult { match value { - FloatValue::Untyped(x) => Ok(x.into_fallback() as $type), - FloatValue::$variant(x) => Ok(x), + FloatContent::Untyped(x) => Ok(x.into_fallback() as $type), + FloatContent::$variant(x) => Ok(x), other => context.err($articled_display_name, other), } } diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 5abbac09..6fbfc20e 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -26,42 +26,42 @@ impl UntypedFloat { /// Converts an untyped float to a specific float kind. /// Unlike integers, float conversion never fails (may lose precision). - pub(crate) fn into_kind(self, kind: FloatLeafKind) -> FloatValue { + pub(crate) fn into_kind(self, kind: FloatLeafKind) -> OwnedFloatContent { match kind { - FloatLeafKind::Untyped(_) => FloatValue::Untyped(self), - FloatLeafKind::F32(_) => FloatValue::F32(self.0 as f32), - FloatLeafKind::F64(_) => FloatValue::F64(self.0), + FloatLeafKind::Untyped(_) => FloatContent::Untyped(self), + FloatLeafKind::F32(_) => FloatContent::F32(self.0 as f32), + FloatLeafKind::F64(_) => FloatContent::F64(self.0), } } pub(crate) fn paired_operation( self, - rhs: Spanned>, + rhs: Spanned, perform_fn: fn(FallbackFloat, FallbackFloat) -> FallbackFloat, - ) -> ExecutionResult { + ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedFloat = rhs.resolve_as("This operand")?; + let rhs: UntypedFloat = rhs.resolve("This operand")?; let rhs = rhs.0; let output = perform_fn(lhs, rhs); - Ok(FloatValue::Untyped(UntypedFloat::from_fallback(output))) + Ok(FloatContent::Untyped(UntypedFloat::from_fallback(output))) } pub(crate) fn paired_comparison( self, - rhs: Spanned>, + rhs: Spanned, compare_fn: fn(FallbackFloat, FallbackFloat) -> bool, ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedFloat = rhs.resolve_as("This operand")?; + let rhs: UntypedFloat = rhs.resolve("This operand")?; let rhs = rhs.0; Ok(compare_fn(lhs, rhs)) } - pub(super) fn into_fallback(self) -> FallbackFloat { + pub(crate) fn into_fallback(self) -> FallbackFloat { self.0 } - pub(super) fn from_fallback(value: FallbackFloat) -> Self { + pub(crate) fn from_fallback(value: FallbackFloat) -> Self { Self(value) } @@ -201,10 +201,10 @@ impl ResolvableOwned for UntypedFloatFallback { } } -impl ResolvableOwned for UntypedFloat { - fn resolve_from_value(value: FloatValue, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for UntypedFloat { + fn resolve_from_value(value: OwnedFloatContent, context: ResolutionContext) -> ExecutionResult { match value { - FloatValue::Untyped(value) => Ok(value), + FloatContent::Untyped(value) => Ok(value), _ => context.err("an untyped float", value), } } @@ -214,7 +214,7 @@ impl_resolvable_argument_for! { UntypedFloatType, (value, context) -> UntypedFloat { match value { - ValueContent::Float(FloatValue::Untyped(x)) => Ok(x), + ValueContent::Float(FloatContent::Untyped(x)) => Ok(x), other => context.err("an untyped float", other), } } diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 11be17b4..68b67623 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -249,9 +249,9 @@ define_type_features! { Ok(OutputStream::new_with(|s| s.push_tokens(float))) } - [context] fn float(this: Spanned>) -> ExecutionResult { + [context] fn float(this: Spanned>) -> ExecutionResult { let float: syn::LitFloat = parser(this, context)?.parse()?; - Ok(FloatValue::for_litfloat(&float)?.into_inner()) + Ok(OwnedFloat::for_litfloat(&float)?) } } pub(crate) mod unary_operations { diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index 052fafdf..221bde28 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -227,7 +227,7 @@ impl Value { Ok(int) => Some(int.into_owned_value()), Err(_) => None, }, - Lit::Float(lit) => match FloatValue::for_litfloat(lit) { + Lit::Float(lit) => match OwnedFloat::for_litfloat(lit) { Ok(float) => Some(float.into_owned_value()), Err(_) => None, }, diff --git a/tests/operations.rs b/tests/operations.rs index c7968932..a1c5a95a 100644 --- a/tests/operations.rs +++ b/tests/operations.rs @@ -981,6 +981,8 @@ mod float_compound_assignment { #[test] fn add_assign() { assert_eq!(run!(let x = 1.5; x += 2.5; x), 4.0); + assert_eq!(run!(let x = 1.5; x += 2.5f32; x), 4.0); + assert_eq!(run!(let x = 1.5; x += 2.5f64; x), 4.0); assert_eq!(run!(let x = 1.5f32; x += 2.5; x), 4.0f32); assert_eq!(run!(let x = 1.5f64; x += 2.5; x), 4.0f64); } From 8b57f8411b9ea89c3f4f6387b3f8a988d176f3bc Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 6 Jan 2026 00:47:02 +0100 Subject: [PATCH 23/58] fix: An f32 can resolve from an unsuffixed literal --- plans/TODO.md | 7 +--- src/expressions/concepts/actual.rs | 31 +++++++++------- src/expressions/concepts/form.rs | 21 +++++------ src/expressions/concepts/type_traits.rs | 6 ++- src/expressions/operations.rs | 14 +++---- src/expressions/type_resolution/arguments.rs | 3 ++ src/expressions/values/float.rs | 7 ++-- src/expressions/values/float_subtypes.rs | 39 ++++++++++++++++++++ src/expressions/values/float_untyped.rs | 5 ++- src/expressions/values/integer_subtypes.rs | 33 +++++++++++++++++ 10 files changed, 122 insertions(+), 44 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index c67f639b..408f72aa 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -240,12 +240,9 @@ First, read the @./2025-11-vision.md - [ ] Trial getting rid of `Actual` completely? * Maybe it's just a type alias? * Downcast / Upcast become traits - * It might cause issues for e.g. - [ ] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` - - [ ] Create new branch - - [ ] Resolve issue with `2.3` not resolving into `2f32` any more - - [ ] Possibly need an explicit `MaybeTyped<..>` which does not implement `FromValueContent` but - rather has e.g. `MaybeTyped` implement directly `IsArgument` and `ResolveAs> for FloatContent` (if it doesn't conflict?) + - [x] Create new branch + - [x] Resolve issue with `2.3` not resolving into `2f32` any more - [ ] .. same for int... - [ ] Change `type Value<'a, F> = ValueContent<'a, F>` and `type OwnedValue` with: - [ ] `type OwnedValue = QqqOwned` diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs index 7723d95a..e9b4f2da 100644 --- a/src/expressions/concepts/actual.rs +++ b/src/expressions/concepts/actual.rs @@ -21,9 +21,9 @@ impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> { // Hierarchical implementations impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> - where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, +where + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, { #[inline] pub(crate) fn downcast>(self) -> Option> @@ -56,9 +56,9 @@ impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> } impl<'a, T: IsType, F: IsFormOf> Spanned> - where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, +where + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, { pub(crate) fn resolve>( self, @@ -69,22 +69,25 @@ impl<'a, T: IsType, F: IsFormOf> Spanned> >::Type: DowncastFrom, { let Spanned(value, span_range) = self; - let resolved = <>::Type>::resolve(value.0, span_range, description)?; + let resolved = + <>::Type>::resolve(value.0, span_range, description)?; Ok(X::from_content(resolved)) } } // TODO[concepts]: Remove resolve_as eventually? -impl<'a, T: IsType, F: IsFormOf, X: FromValueContent<'a, Form = F>> ResolveAs for Spanned> - where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, - F: IsFormOf<>::Type>, - >::Type: DowncastFrom, +impl<'a, T: IsType, F: IsFormOf, X: FromValueContent<'a, Form = F>> ResolveAs + for Spanned> +where + for<'l> T: IsHierarchicalType = >::Content<'l>>, + F: IsHierarchicalForm, + F: IsFormOf<>::Type>, + >::Type: DowncastFrom, { fn resolve_as(self, description: &str) -> ExecutionResult { let Spanned(value, span_range) = self; - let resolved = <>::Type>::resolve(value.0, span_range, description)?; + let resolved = + <>::Type>::resolve(value.0, span_range, description)?; Ok(X::from_content(resolved)) } } diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index b0241db5..81dea689 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -92,12 +92,11 @@ pub(crate) trait MapIntoReturned: IsFormOf { // } // TODO[concepts]: Replace with the above impl when ready -impl< - F: IsFormOf + MapFromArgument, - T: TypeData + DowncastFrom, -> IsArgument for Actual<'static, T, F> +impl + MapFromArgument, T: TypeData + DowncastFrom> IsArgument + for Actual<'static, T, F> where - for<'l> ValueType: IsHierarchicalType = >::Content<'l>>, + for<'l> ValueType: + IsHierarchicalType = >::Content<'l>>, F: IsHierarchicalForm, { type ValueType = T; @@ -124,13 +123,11 @@ where // } // TODO[concepts]: Replace with the above impl when ready -impl< - F: IsFormOf + MapIntoReturned, - T: UpcastTo, -> IsReturnable for Actual<'static, T, F> { +impl + MapIntoReturned, T: UpcastTo> IsReturnable + for Actual<'static, T, F> +{ fn to_returned_value(self) -> ExecutionResult { - let type_mapped = self.into_actual() - .upcast::(); + let type_mapped = self.into_actual().upcast::(); F::into_returned_value(type_mapped) } -} \ No newline at end of file +} diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 531de00e..6694b92c 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -84,8 +84,10 @@ pub(crate) trait UpcastTo + IsFormOf>: IsType { ) -> >::Content<'a>; } -pub(crate) trait DowncastFrom + IsFormOf>: IsType -where +pub(crate) trait DowncastFrom< + T: IsHierarchicalType, + F: IsHierarchicalForm + IsFormOf + IsFormOf, +>: IsType where for<'l> T: IsHierarchicalType = >::Content<'l>>, { fn downcast_from<'a>( diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index 21bfa5a6..2d248a03 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -430,35 +430,35 @@ pub(super) trait HandleBinaryOperation: Sized + std::fmt::Display + Copy { fn paired_operation>( self, - rhs: impl ResolveAs, + rhs: impl ResolveAs>, context: BinaryOperationCallContext, perform_fn: fn(Self, Self) -> Option, ) -> ExecutionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; - perform_fn(lhs, rhs) + perform_fn(lhs, rhs.0) .map(|r| r.into()) - .ok_or_else(|| Self::binary_overflow_error(context, lhs, rhs)) + .ok_or_else(|| Self::binary_overflow_error(context, lhs, rhs.0)) } fn paired_operation_no_overflow>( self, - rhs: impl ResolveAs, + rhs: impl ResolveAs>, perform_fn: fn(Self, Self) -> Self, ) -> ExecutionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; - Ok(perform_fn(lhs, rhs).into()) + Ok(perform_fn(lhs, rhs.0).into()) } fn paired_comparison( self, - rhs: impl ResolveAs, + rhs: impl ResolveAs>, compare_fn: fn(Self, Self) -> bool, ) -> ExecutionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; - Ok(compare_fn(lhs, rhs)) + Ok(compare_fn(lhs, rhs.0)) } fn shift_operation>( diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index 7bc823c3..51a21965 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -2,6 +2,9 @@ // TODO[unused-clearup] use super::*; +/// Used for resolution of integers and floats from either typed or untyped literals. +pub(crate) struct OptionalSuffix(pub(crate) T); + pub(crate) struct ResolutionContext<'a> { span_range: &'a SpanRange, resolution_target: &'a str, diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index f6a2b0ec..0010d87d 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -101,7 +101,6 @@ impl OwnedFloatContent { } } - fn assign_op( mut left: Assignee, right: R, @@ -114,7 +113,6 @@ fn assign_op( Ok(()) } - impl Debug for OwnedFloatContent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -127,7 +125,10 @@ impl Debug for OwnedFloatContent { /// Aligns types for comparison - converts untyped to match the other's type. /// Unlike integers, float conversion never fails (may lose precision). -fn align_types(mut lhs: OwnedFloatContent, mut rhs: OwnedFloatContent) -> (OwnedFloatContent, OwnedFloatContent) { +fn align_types( + mut lhs: OwnedFloatContent, + mut rhs: OwnedFloatContent, +) -> (OwnedFloatContent, OwnedFloatContent) { match (&lhs, &rhs) { (FloatContent::Untyped(l), typed) if !matches!(typed, FloatContent::Untyped(_)) => { lhs = l.into_kind(typed.kind()); diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 7847419d..a60f5004 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -158,6 +158,45 @@ macro_rules! impl_resolvable_float_subtype { dyn_impls: {}, } + impl IsArgument for OptionalSuffix<$type> { + type ValueType = FloatType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + + fn from_argument(argument: Spanned) -> ExecutionResult { + argument.expect_owned().resolve_as("This argument") + } + } + + impl ResolveAs> for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + let span = self.span_range(); + let float_value: OwnedFloatContent = self.resolve_as(resolution_target)?; + Spanned(float_value, span).resolve_as(resolution_target) + } + } + + impl ResolveAs> for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + self.map(|float| float.0).resolve_as(resolution_target) + } + } + + impl ResolveAs> for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + let Spanned(value, span) = self; + match value { + FloatContent::Untyped(v) => Ok(OptionalSuffix(v.into_fallback() as $type)), + FloatContent::$variant(v) => Ok(OptionalSuffix(v)), + v => span.type_err(format!( + "{} is expected to be {}, but it is {}", + resolution_target, + $kind.articled_display_name(), + v.articled_kind(), + )), + } + } + } + impl ResolvableArgumentTarget for $type { type ValueType = $type_def; } diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 6fbfc20e..a0397f30 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -202,7 +202,10 @@ impl ResolvableOwned for UntypedFloatFallback { } impl ResolvableOwned for UntypedFloat { - fn resolve_from_value(value: OwnedFloatContent, context: ResolutionContext) -> ExecutionResult { + fn resolve_from_value( + value: OwnedFloatContent, + context: ResolutionContext, + ) -> ExecutionResult { match value { FloatContent::Untyped(value) => Ok(value), _ => context.err("an untyped float", value), diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 43492c9d..18578155 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -192,6 +192,39 @@ macro_rules! impl_resolvable_integer_subtype { dyn_impls: {}, } + impl IsArgument for OptionalSuffix<$type> { + type ValueType = IntegerType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + + fn from_argument(argument: Spanned) -> ExecutionResult { + argument.expect_owned().resolve_as("This argument") + } + } + + impl ResolveAs> for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + let span = self.span_range(); + let integer_value: Owned = self.resolve_as(resolution_target)?; + Spanned(integer_value, span).resolve_as(resolution_target) + } + } + + impl ResolveAs> for Spanned> { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + let Spanned(value, span) = self; + match value.0 { + IntegerValue::Untyped(v) => Ok(OptionalSuffix(v.into_fallback() as $type)), + IntegerValue::$variant(v) => Ok(OptionalSuffix(v)), + v => span.type_err(format!( + "{} is expected to be {}, but it is {}", + resolution_target, + $kind.articled_display_name(), + v.articled_kind(), + )), + } + } + } + impl ResolvableArgumentTarget for $type { type ValueType = $type_def; } From 14e7a499493a74e3840d2ed65a8442d7ef6c1d6c Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 6 Jan 2026 00:52:06 +0100 Subject: [PATCH 24/58] fix: Fix MSRV build --- src/expressions/concepts/form.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 81dea689..3097fe78 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -123,11 +123,13 @@ where // } // TODO[concepts]: Replace with the above impl when ready -impl + MapIntoReturned, T: UpcastTo> IsReturnable - for Actual<'static, T, F> -{ - fn to_returned_value(self) -> ExecutionResult { - let type_mapped = self.into_actual().upcast::(); - F::into_returned_value(type_mapped) - } -} +// This conflicts with the impl `impl IsReturnable for T {` on MSRV +// and isn't needed right now... so leave it commented out. +// impl + MapIntoReturned, T: UpcastTo> IsReturnable +// for Actual<'static, T, F> +// { +// fn to_returned_value(self) -> ExecutionResult { +// let type_mapped = self.into_actual().upcast::(); +// F::into_returned_value(type_mapped) +// } +// } From 58b3ecde5e74a8cf24dddc2efab5c79eff6921e4 Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 6 Jan 2026 03:16:00 +0100 Subject: [PATCH 25/58] tweak: Removed Actual wrapper type --- plans/TODO.md | 5 +- src/expressions/concepts/actual.rs | 194 ----------------- src/expressions/concepts/content.rs | 204 ++++++++++++++++++ src/expressions/concepts/form.rs | 59 ----- src/expressions/concepts/forms/owned.rs | 30 ++- .../concepts/forms/referenceable.rs | 13 +- src/expressions/concepts/forms/simple_mut.rs | 13 -- src/expressions/concepts/forms/simple_ref.rs | 13 -- src/expressions/concepts/mod.rs | 4 +- src/expressions/patterns.rs | 8 +- src/expressions/values/float.rs | 86 ++++---- src/expressions/values/float_subtypes.rs | 14 +- src/expressions/values/float_untyped.rs | 12 +- 13 files changed, 280 insertions(+), 375 deletions(-) delete mode 100644 src/expressions/concepts/actual.rs create mode 100644 src/expressions/concepts/content.rs diff --git a/plans/TODO.md b/plans/TODO.md index 408f72aa..edc3ac8e 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -237,9 +237,7 @@ First, read the @./2025-11-vision.md - [ ] 6 methods... `map_content`, `map_content_ref`, `map_content_mut` - [ ] ... and a `ReduceMapper::::map(content, |x| -> y)` - [ ] ... Probably remove e.g. `map_with` etc on actual? - - [ ] Trial getting rid of `Actual` completely? - * Maybe it's just a type alias? - * Downcast / Upcast become traits + - [x] Trial getting rid of `Actual` completely? - [ ] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` - [x] Create new branch - [x] Resolve issue with `2.3` not resolving into `2f32` any more @@ -249,6 +247,7 @@ First, read the @./2025-11-vision.md - [ ] `type ValueRef = QqqRef` - [ ] `type ValueMut = QqqMut` - [ ] ... and move methods + - [ ] Rename `ValueType` to `AnyType` and `Value` to `AnyValue` - [ ] Replace `Owned` as `QqqOwned` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` - [ ] And similarly for other values... diff --git a/src/expressions/concepts/actual.rs b/src/expressions/concepts/actual.rs deleted file mode 100644 index e9b4f2da..00000000 --- a/src/expressions/concepts/actual.rs +++ /dev/null @@ -1,194 +0,0 @@ -use super::*; - -#[derive(Copy, Clone)] -pub(crate) struct Actual<'a, T: IsType, F: IsFormOf>(pub(crate) F::Content<'a>); - -impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> { - #[inline] - pub(crate) fn of(content: impl IntoValueContent<'a, Type = T, Form = F>) -> Self { - Actual(content.into_content()) - } - - #[inline] - pub(crate) fn upcast(self) -> Actual<'a, S, F> - where - F: IsFormOf, - T: UpcastTo, - { - Actual(T::upcast_to(self.0)) - } -} - -// Hierarchical implementations -impl<'a, T: IsType, F: IsFormOf> Actual<'a, T, F> -where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, -{ - #[inline] - pub(crate) fn downcast>(self) -> Option> - where - F: IsFormOf, - { - Some(Actual(U::downcast_from(self.0)?)) - } - - #[inline] - pub(crate) fn map_with>( - self, - ) -> Result, M::ShortCircuit<'a>> { - T::map_with::<'a, F, M>(self.0).map(|c| Actual(c)) - } - - #[inline] - pub(crate) fn map_ref_with<'r, M: RefLeafMapper>( - &'r self, - ) -> Result, M::ShortCircuit<'a>> { - T::map_ref_with::(&self.0).map(|c| Actual(c)) - } - - #[inline] - pub(crate) fn map_mut_with<'r, M: MutLeafMapper>( - &'r mut self, - ) -> Result, M::ShortCircuit<'a>> { - T::map_mut_with::(&mut self.0).map(|c| Actual(c)) - } -} - -impl<'a, T: IsType, F: IsFormOf> Spanned> -where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, -{ - pub(crate) fn resolve>( - self, - description: &str, - ) -> ExecutionResult - where - F: IsFormOf<>::Type>, - >::Type: DowncastFrom, - { - let Spanned(value, span_range) = self; - let resolved = - <>::Type>::resolve(value.0, span_range, description)?; - Ok(X::from_content(resolved)) - } -} - -// TODO[concepts]: Remove resolve_as eventually? -impl<'a, T: IsType, F: IsFormOf, X: FromValueContent<'a, Form = F>> ResolveAs - for Spanned> -where - for<'l> T: IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, - F: IsFormOf<>::Type>, - >::Type: DowncastFrom, -{ - fn resolve_as(self, description: &str) -> ExecutionResult { - let Spanned(value, span_range) = self; - let resolved = - <>::Type>::resolve(value.0, span_range, description)?; - Ok(X::from_content(resolved)) - } -} - -impl<'a, T: IsType + UpcastTo, F: IsFormOf + IsFormOf> - Actual<'a, T, F> -{ - pub(crate) fn into_value(self) -> Actual<'a, ValueType, F> { - self.upcast() - } -} - -impl<'a, T: IsType, F: IsFormOf> Deref for Actual<'a, T, F> { - type Target = F::Content<'a>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, T: IsType, F: IsFormOf> DerefMut for Actual<'a, T, F> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl<'a, T: IsHierarchicalType, F: IsFormOf> HasLeafKind for Actual<'a, T, F> -where - F: IsHierarchicalForm, - for<'l> T: IsHierarchicalType = >::Content<'l>>, -{ - type LeafKind = ::LeafKind; - - fn kind(&self) -> Self::LeafKind { - ::content_to_leaf_kind::(&self.0) - } -} - -pub(crate) trait IsValueContent<'a> { - type Type: IsType; - type Form: IsFormOf; -} - -pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { - fn into_content(self) -> >::Content<'a>; - - #[inline] - fn into_actual(self) -> Actual<'a, Self::Type, Self::Form> - where - Self: Sized, - { - Actual::of(self) - } - - #[inline] - fn into_actual_value(self) -> Actual<'a, ValueType, Self::Form> - where - Self: Sized, - Self::Type: UpcastTo, - Self::Form: IsFormOf, - { - Actual::of(self).into_value() - } -} - -// TODO[concepts]: Remove eventually, along with IntoValue impl -impl> IntoValue for X -where - X::Type: UpcastTo, - BeOwned: IsFormOf, -{ - fn into_value(self) -> Value { - self.into_actual_value().0 - } -} - -pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { - fn from_content(content: >::Content<'a>) -> Self; - - #[inline] - fn from_actual(actual: Actual<'a, Self::Type, Self::Form>) -> Self - where - Self: Sized, - { - Self::from_content(actual.0) - } -} - -impl<'a, T: IsType, F: IsFormOf> IsValueContent<'a> for Actual<'a, T, F> { - type Type = T; - type Form = F; -} - -impl<'a, T: IsType, F: IsFormOf> FromValueContent<'a> for Actual<'a, T, F> { - fn from_content(content: >::Content<'a>) -> Self { - Actual(content) - } -} - -impl<'a, T: IsType, F: IsFormOf> IntoValueContent<'a> for Actual<'a, T, F> { - fn into_content(self) -> >::Content<'a> { - self.0 - } -} diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs new file mode 100644 index 00000000..c5e1cf41 --- /dev/null +++ b/src/expressions/concepts/content.rs @@ -0,0 +1,204 @@ +use super::*; + +/// Shorthand for representing the form F of a type T with a particular lifetime 'a. +pub(crate) type Actual<'a, T, F> = >::Content<'a>; + +/// For types which have an associated value (type and form) +pub(crate) trait IsValueContent<'a> { + type Type: IsType; + type Form: IsFormOf; +} + +pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { + fn from_content(content: >::Content<'a>) -> Self; +} + +pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { + fn into_content(self) -> >::Content<'a>; + + #[inline] + fn upcast(self) -> Actual<'a, S, Self::Form> + where + Self: Sized, + Self::Form: IsFormOf, + Self::Type: UpcastTo, + { + >::upcast_to(self.into_content()) + } + + #[inline] + fn downcast>(self) -> Option> + where + Self: Sized, + Self::Form: IsFormOf, + for<'l> Self::Type: IsHierarchicalType = >::Content<'l>>, + Self::Form: IsHierarchicalForm, + { + U::downcast_from(self.into_content()) + } + + #[inline] + fn into_any(self) -> Actual<'a, ValueType, Self::Form> + where + Self: Sized, + Self::Type: UpcastTo, + Self::Form: IsFormOf, + { + self.upcast() + } + + fn map_with>( + self, + ) -> Result, M::ShortCircuit<'a>> + where + Self: Sized, + Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Form: IsHierarchicalForm, + { + ::map_with::(self.into_content()) + } + + fn into_referenceable(self) -> Actual<'a, Self::Type, BeReferenceable> + where + Self: Sized, + Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Form: IsHierarchicalForm, + for<'l> OwnedToReferenceableMapper: LeafMapper = Infallible>, + { + match self.map_with::() { + Ok(output) => output, + Err(infallible) => match infallible {}, // Need to include because of MSRV + } + } +} + +impl<'a, C> Spanned +where + C: IntoValueContent<'a>, + for<'l> C::Type: IsHierarchicalType = >::Content<'l>>, + C::Form: IsHierarchicalForm, +{ + pub(crate) fn downcast_resolve>(self, description: &str) -> ExecutionResult + where + C::Form: IsFormOf<>::Type>, + >::Type: DowncastFrom + { + let Spanned(value, span_range) = self; + let content = value.into_content(); + let resolved = + <>::Type>::resolve(content, span_range, description)?; + Ok(X::from_content(resolved)) + } +} + +// TODO[concepts]: Remove eventually, along with IntoValue impl +impl> IntoValue for X +where + X::Type: UpcastTo, + BeOwned: IsFormOf, +{ + fn into_value(self) -> Value { + self.into_any() + } +} + +/// Implementations on the value contents *themselves*. +/// Typically this is reserved for things operating on references - otherwise we can +/// implement on IntoValueContent / FromValueContent. +pub(crate) trait IsSelfValueContent<'a>: IsValueContent<'a> +where + Self::Form: IsFormOf = Self>, +{ + fn map_mut_with<'r, M: MutLeafMapper>( + &'r mut self, + ) -> Result, M::ShortCircuit<'a>> + where + 'a: 'r, + Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Form: IsHierarchicalForm, + { + ::map_mut_with::(self) + } + + fn map_ref_with<'r, M: RefLeafMapper>( + &'r self, + ) -> Result, M::ShortCircuit<'a>> + where + 'a: 'r, + Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Form: IsHierarchicalForm, + { + ::map_ref_with::(self) + } + + fn as_mut_value<'r>(&'r mut self) -> Actual<'r, Self::Type, BeMut> + where + // Bounds for map_mut_with to work + 'a: 'r, + Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Form: IsHierarchicalForm, + + // Bounds for ToMutMapper to work + Self::Form: LeafAsMutForm, + BeMut: IsFormOf, + { + match Self::map_mut_with::(self) { + Ok(x) => x, + Err(infallible) => match infallible {}, // Need to include because of MSRV + } + } + + fn as_ref_value<'r>(&'r self) -> Actual<'r, Self::Type, BeRef> + where + // Bounds for map_mut_with to work + 'a: 'r, + Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Form: IsHierarchicalForm, + + // Bounds for ToMutMapper to work + Self::Form: LeafAsRefForm, + BeMut: IsFormOf, + { + match Self::map_ref_with::(self) { + Ok(x) => x, + Err(infallible) => match infallible {}, // Need to include because of MSRV + } + } +} + +impl<'a, X> IsSelfValueContent<'a> for X +where + X: IsValueContent<'a>, + X::Form: IsFormOf = X>, +{ +} + +// Clashes with other blanket impl it will replace! +// +// impl< +// X: FromValueContent<'static, Type = T, Form = F>, +// F: IsForm + MapFromArgument, +// T: TypeData + DowncastFrom, +// > IsArgument for X { +// type ValueType = T; +// const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; +// fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { +// let ownership_mapped = F::from_argument_value(value)?; +// let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; +// Ok(X::from_actual(type_mapped)) +// } +// } + +// Clashes with other blanket impl it will replace! +// +// impl< +// X: IntoValueContent<'static, Type = T, Form = F>, +// F: IsForm + MapIntoReturned, +// T: UpcastTo, +// > IsReturnable for X { +// fn to_returned_value(self) -> ExecutionResult { +// let type_mapped = self.into_actual() +// .upcast::(); +// F::into_returned_value(type_mapped) +// } +// } \ No newline at end of file diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 3097fe78..56279cbb 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -74,62 +74,3 @@ pub(crate) trait MapIntoReturned: IsFormOf { value: Actual<'static, ValueType, Self>, ) -> ExecutionResult; } - -// Clashes with other blanket impl it will replace! -// -// impl< -// X: FromValueContent<'static, Type = T, Form = F>, -// F: IsForm + MapFromArgument, -// T: TypeData + DowncastFrom, -// > IsArgument for X { -// type ValueType = T; -// const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; -// fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { -// let ownership_mapped = F::from_argument_value(value)?; -// let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; -// Ok(X::from_actual(type_mapped)) -// } -// } - -// TODO[concepts]: Replace with the above impl when ready -impl + MapFromArgument, T: TypeData + DowncastFrom> IsArgument - for Actual<'static, T, F> -where - for<'l> ValueType: - IsHierarchicalType = >::Content<'l>>, - F: IsHierarchicalForm, -{ - type ValueType = T; - const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; - fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { - let ownership_mapped = F::from_argument_value(value)?; - let type_mapped = T::resolve(ownership_mapped.0, span_range, "This argument")?; - Ok(Actual(type_mapped)) - } -} - -// Clashes with other blanket impl it will replace! -// -// impl< -// X: IntoValueContent<'static, Type = T, Form = F>, -// F: IsForm + MapIntoReturned, -// T: UpcastTo, -// > IsReturnable for X { -// fn to_returned_value(self) -> ExecutionResult { -// let type_mapped = self.into_actual() -// .upcast::(); -// F::into_returned_value(type_mapped) -// } -// } - -// TODO[concepts]: Replace with the above impl when ready -// This conflicts with the impl `impl IsReturnable for T {` on MSRV -// and isn't needed right now... so leave it commented out. -// impl + MapIntoReturned, T: UpcastTo> IsReturnable -// for Actual<'static, T, F> -// { -// fn to_returned_value(self) -> ExecutionResult { -// let type_mapped = self.into_actual().upcast::(); -// F::into_returned_value(type_mapped) -// } -// } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index c5eac5fd..9fd4a154 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -1,16 +1,14 @@ use super::*; -/// A semantic wrapper for floating owned values. -/// -/// Can be destructured as: `Owned(value): Owned` -/// -/// If you need span information, wrap with `Spanned>`. For example, for `x.y[4]`, this would capture both: -/// * The output owned value -/// * The lexical span of the tokens `x.y[4]` pub(crate) type QqqOwned = Actual<'static, T, BeOwned>; pub(crate) type QqqOwnedValue = Owned; +/// Represents floating owned values. +/// +/// If you need span information, wrap with `Spanned`. For example, with `x.y[4]`, this would capture both: +/// * The output owned value +/// * The lexical span of the tokens `x.y[4]` #[derive(Copy, Clone)] pub(crate) struct BeOwned; impl IsForm for BeOwned {} @@ -49,7 +47,7 @@ impl MapFromArgument for BeOwned { fn from_argument_value( value: ArgumentValue, ) -> ExecutionResult> { - Ok(value.expect_owned().0.into_actual()) + Ok(value.expect_owned().0) } } @@ -59,25 +57,25 @@ mod test { #[test] fn can_resolve_owned() { - let owned_value: QqqOwned = QqqOwned::of(42u64); + let owned_value: QqqOwned = 42u64; let resolved = owned_value .spanned(Span::call_site().span_range()) - .resolve::("My value") + .downcast_resolve::("My value") .unwrap(); assert_eq!(resolved, 42u64); } #[test] fn can_as_ref_owned() { - let owned_value: QqqOwned = QqqOwned::of(42u64); - let as_ref: QqqRef = owned_value.as_ref(); - assert_eq!(**as_ref, 42u64); + let owned_value: QqqOwned = 42u64; + let as_ref: QqqRef = owned_value.as_ref_value(); + assert_eq!(*as_ref, 42u64); } #[test] fn can_as_mut_owned() { - let mut owned_value: QqqOwned = QqqOwned::of(42u64); - **owned_value.as_mut() = 41u64; - assert_eq!(owned_value.0, 41u64); + let mut owned_value: QqqOwned = 42u64; + *owned_value.as_mut_value() = 41u64; + assert_eq!(owned_value, 41u64); } } diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index 13e8e8b9..fba3e6d3 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -26,18 +26,9 @@ impl MapFromArgument for BeReferenceable { } } -impl<'a, T: IsHierarchicalType> Actual<'a, T, BeOwned> { - pub(crate) fn into_referencable(self) -> Actual<'a, T, BeReferenceable> { - match self.map_with::() { - Ok(output) => output, - Err(infallible) => match infallible {}, // Need to include because of MSRV - } - } -} - -pub(crate) struct OwnedToReferencableMapper; +pub(crate) struct OwnedToReferenceableMapper; -impl LeafMapper for OwnedToReferencableMapper { +impl LeafMapper for OwnedToReferenceableMapper { type OutputForm = BeReferenceable; type ShortCircuit<'a> = Infallible; diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index 1cb63d61..9bb7a3ce 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -43,16 +43,3 @@ impl MutLeafMapper for ToMutMapper { Ok(F::leaf_as_mut(leaf)) } } - -impl<'a, T: IsHierarchicalType, F: IsFormOf> Actual<'a, T, F> -where - F: LeafAsMutForm, - for<'l> T: IsHierarchicalType = >::Content<'l>>, -{ - pub(crate) fn as_mut<'r>(&'r mut self) -> Actual<'r, T, BeMut> { - match self.map_mut_with::() { - Ok(x) => x, - Err(infallible) => match infallible {}, // Need to include because of MSRV - } - } -} diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 7e5bdd32..4628421d 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -35,16 +35,3 @@ impl RefLeafMapper for ToRefMapper { Ok(F::leaf_as_ref(leaf)) } } - -impl<'a, T: IsHierarchicalType, F: IsFormOf> Actual<'a, T, F> -where - F: LeafAsRefForm, - for<'l> T: IsHierarchicalType = >::Content<'l>>, -{ - pub(crate) fn as_ref<'r>(&'r self) -> Actual<'r, T, BeRef> { - match self.map_ref_with::() { - Ok(x) => x, - Err(infallible) => match infallible {}, // Need to include because of MSRV - } - } -} diff --git a/src/expressions/concepts/mod.rs b/src/expressions/concepts/mod.rs index 6ec59a94..7cedeec5 100644 --- a/src/expressions/concepts/mod.rs +++ b/src/expressions/concepts/mod.rs @@ -1,12 +1,12 @@ #![allow(dead_code)] // Whilst we're building it out use super::*; -mod actual; +mod content; mod form; mod forms; mod type_traits; -pub(crate) use actual::*; +pub(crate) use content::*; pub(crate) use form::*; pub(crate) use forms::*; pub(crate) use type_traits::*; diff --git a/src/expressions/patterns.rs b/src/expressions/patterns.rs index 34b9774d..338b80a1 100644 --- a/src/expressions/patterns.rs +++ b/src/expressions/patterns.rs @@ -354,8 +354,8 @@ impl HandleDestructure for StreamPattern { interpreter: &mut Interpreter, value: Value, ) -> ExecutionResult<()> { - let stream = Spanned(Actual::of(value), self.brackets.span_range()) - .resolve("The value destructured with a stream pattern")?; + let stream = Spanned(value, self.brackets.span_range()) + .downcast_resolve("The value destructured with a stream pattern")?; interpreter.start_parse(stream, |interpreter, _| self.content.consume(interpreter)) } } @@ -396,8 +396,8 @@ impl HandleDestructure for ParseTemplatePattern { interpreter: &mut Interpreter, value: Value, ) -> ExecutionResult<()> { - let stream = Spanned(Actual::of(value), self.brackets.span_range()) - .resolve("The value destructured with a parse template pattern")?; + let stream = Spanned(value, self.brackets.span_range()) + .downcast_resolve("The value destructured with a parse template pattern")?; interpreter.start_parse(stream, |interpreter, handle| { self.parser_definition.define(interpreter, handle); self.content.consume(interpreter) diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index 0010d87d..0eb96d3a 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -14,9 +14,8 @@ define_parent_type! { articled_display_name: "a float", } -pub(crate) type OwnedFloat = QqqOwned; -pub(crate) type OwnedFloatContent = FloatContent<'static, BeOwned>; -pub(crate) type FloatRef<'a> = QqqRef<'a, FloatType>; +pub(crate) type OwnedFloat = FloatContent<'static, BeOwned>; +pub(crate) type FloatRef<'a> = FloatContent<'a, BeRef>; impl OwnedFloat { pub(super) fn for_litfloat(lit: &syn::LitFloat) -> ParseResult { @@ -29,12 +28,11 @@ impl OwnedFloat { "The literal suffix {suffix} is not supported in preinterpret expressions" )); } - } - .into_actual()) + }) } - pub(crate) fn resolve_untyped_to_match(self, target: FloatRef) -> OwnedFloatContent { - match self.0 { + pub(crate) fn resolve_untyped_to_match(self, target: FloatRef) -> OwnedFloat { + match self { FloatContent::Untyped(this) => this.into_owned().into_kind(target.kind()), other => other, } @@ -46,7 +44,7 @@ impl OwnedFloat { } fn to_unspanned_literal(self) -> Literal { - match self.0 { + match self { FloatContent::Untyped(float) => float.to_unspanned_literal(), FloatContent::F32(float) => Literal::f32_suffixed(float), FloatContent::F64(float) => Literal::f64_suffixed(float), @@ -55,7 +53,7 @@ impl OwnedFloat { } // TODO[concepts]: Move to FloatRef<'a> when we can -impl OwnedFloatContent { +impl OwnedFloat { /// Outputs this float value to a token stream. /// For finite values, outputs a literal. For non-finite values (infinity, NaN), /// outputs the equivalent constant path like `f32::INFINITY`. @@ -102,18 +100,18 @@ impl OwnedFloatContent { } fn assign_op( - mut left: Assignee, + mut left: Assignee, right: R, context: BinaryOperationCallContext, - op: fn(BinaryOperationCallContext, OwnedFloat, R) -> ExecutionResult, + op: fn(BinaryOperationCallContext, OwnedFloat, R) -> ExecutionResult, ) -> ExecutionResult<()> { let left_value = core::mem::replace(&mut *left, FloatContent::F32(0.0)); - let result = op(context, left_value.into_actual(), right)?; + let result = op(context, left_value, right)?; *left = result; Ok(()) } -impl Debug for OwnedFloatContent { +impl Debug for OwnedFloat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { FloatContent::Untyped(v) => write!(f, "{}", v.into_fallback()), @@ -126,9 +124,9 @@ impl Debug for OwnedFloatContent { /// Aligns types for comparison - converts untyped to match the other's type. /// Unlike integers, float conversion never fails (may lose precision). fn align_types( - mut lhs: OwnedFloatContent, - mut rhs: OwnedFloatContent, -) -> (OwnedFloatContent, OwnedFloatContent) { + mut lhs: OwnedFloat, + mut rhs: OwnedFloat, +) -> (OwnedFloat, OwnedFloat) { match (&lhs, &rhs) { (FloatContent::Untyped(l), typed) if !matches!(typed, FloatContent::Untyped(_)) => { lhs = l.into_kind(typed.kind()); @@ -142,7 +140,7 @@ fn align_types( } // TODO[concepts]: Should really be over FloatRef<'a> -impl ValuesEqual for OwnedFloatContent { +impl ValuesEqual for OwnedFloat { /// Handles type coercion between typed and untyped floats. /// Uses Rust's float `==`, so `NaN != NaN`. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { @@ -176,7 +174,7 @@ define_type_features! { pub(crate) mod float_interface { pub(crate) mod methods { fn is_nan(this: OwnedFloat) -> bool { - match this.0 { + match this { FloatContent::Untyped(x) => x.into_fallback().is_nan(), FloatContent::F32(x) => x.is_nan(), FloatContent::F64(x) => x.is_nan(), @@ -184,7 +182,7 @@ define_type_features! { } fn is_infinite(this: OwnedFloat) -> bool { - match this.0 { + match this { FloatContent::Untyped(x) => x.into_fallback().is_infinite(), FloatContent::F32(x) => x.is_infinite(), FloatContent::F64(x) => x.is_infinite(), @@ -192,7 +190,7 @@ define_type_features! { } fn is_finite(this: OwnedFloat) -> bool { - match this.0 { + match this { FloatContent::Untyped(x) => x.into_fallback().is_finite(), FloatContent::F32(x) => x.is_finite(), FloatContent::F64(x) => x.is_finite(), @@ -200,7 +198,7 @@ define_type_features! { } fn is_sign_positive(this: OwnedFloat) -> bool { - match this.0 { + match this { FloatContent::Untyped(x) => x.into_fallback().is_sign_positive(), FloatContent::F32(x) => x.is_sign_positive(), FloatContent::F64(x) => x.is_sign_positive(), @@ -208,7 +206,7 @@ define_type_features! { } fn is_sign_negative(this: OwnedFloat) -> bool { - match this.0 { + match this { FloatContent::Untyped(x) => x.into_fallback().is_sign_negative(), FloatContent::F32(x) => x.is_sign_negative(), FloatContent::F64(x) => x.is_sign_negative(), @@ -218,68 +216,68 @@ define_type_features! { pub(crate) mod unary_operations { } pub(crate) mod binary_operations { - fn add(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + fn add(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a + b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a + b), FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a + b), } } - [context] fn add_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn add_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, add) } - fn sub(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + fn sub(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a - b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a - b), FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a - b), } } - [context] fn sub_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn sub_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, sub) } - fn mul(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + fn mul(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a * b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a * b), FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a * b), } } - [context] fn mul_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn mul_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, mul) } - fn div(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + fn div(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a / b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a / b), FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a / b), } } - [context] fn div_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn div_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, div) } - fn rem(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + fn rem(left: OwnedFloat, right: Spanned) -> ExecutionResult { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a % b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a % b), FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a % b), } } - [context] fn rem_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn rem_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, rem) } fn lt(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a < b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a < b), FloatContent::F64(left) => left.paired_comparison(right, |a, b| a < b), @@ -287,7 +285,7 @@ define_type_features! { } fn le(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a <= b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a <= b), FloatContent::F64(left) => left.paired_comparison(right, |a, b| a <= b), @@ -295,7 +293,7 @@ define_type_features! { } fn gt(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a > b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a > b), FloatContent::F64(left) => left.paired_comparison(right, |a, b| a > b), @@ -303,7 +301,7 @@ define_type_features! { } fn ge(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a >= b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a >= b), FloatContent::F64(left) => left.paired_comparison(right, |a, b| a >= b), @@ -311,7 +309,7 @@ define_type_features! { } fn eq(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a == b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a == b), FloatContent::F64(left) => left.paired_comparison(right, |a, b| a == b), @@ -319,7 +317,7 @@ define_type_features! { } fn ne(left: OwnedFloat, right: Spanned) -> ExecutionResult { - match left.resolve_untyped_to_match(right.as_ref()) { + match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a != b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a != b), FloatContent::F64(left) => left.paired_comparison(right, |a, b| a != b), @@ -359,7 +357,7 @@ define_type_features! { impl_resolvable_argument_for! { FloatType, - (value, context) -> OwnedFloatContent { + (value, context) -> OwnedFloat { match value { Value::Float(value) => Ok(value), other => context.err("a float", other), diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index a60f5004..8458bc52 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -170,18 +170,12 @@ macro_rules! impl_resolvable_float_subtype { impl ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { let span = self.span_range(); - let float_value: OwnedFloatContent = self.resolve_as(resolution_target)?; + let float_value: OwnedFloat = self.resolve_as(resolution_target)?; Spanned(float_value, span).resolve_as(resolution_target) } } impl ResolveAs> for Spanned { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { - self.map(|float| float.0).resolve_as(resolution_target) - } - } - - impl ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { let Spanned(value, span) = self; match value { @@ -201,15 +195,15 @@ macro_rules! impl_resolvable_float_subtype { type ValueType = $type_def; } - impl From<$type> for OwnedFloatContent { + impl From<$type> for OwnedFloat { fn from(value: $type) -> Self { FloatContent::$variant(value) } } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - value: OwnedFloatContent, + value: OwnedFloat, context: ResolutionContext, ) -> ExecutionResult { match value { diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index a0397f30..536cc1e3 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -26,7 +26,7 @@ impl UntypedFloat { /// Converts an untyped float to a specific float kind. /// Unlike integers, float conversion never fails (may lose precision). - pub(crate) fn into_kind(self, kind: FloatLeafKind) -> OwnedFloatContent { + pub(crate) fn into_kind(self, kind: FloatLeafKind) -> OwnedFloat { match kind { FloatLeafKind::Untyped(_) => FloatContent::Untyped(self), FloatLeafKind::F32(_) => FloatContent::F32(self.0 as f32), @@ -38,9 +38,9 @@ impl UntypedFloat { self, rhs: Spanned, perform_fn: fn(FallbackFloat, FallbackFloat) -> FallbackFloat, - ) -> ExecutionResult { + ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedFloat = rhs.resolve("This operand")?; + let rhs: UntypedFloat = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; let output = perform_fn(lhs, rhs); Ok(FloatContent::Untyped(UntypedFloat::from_fallback(output))) @@ -52,7 +52,7 @@ impl UntypedFloat { compare_fn: fn(FallbackFloat, FallbackFloat) -> bool, ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedFloat = rhs.resolve("This operand")?; + let rhs: UntypedFloat = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; Ok(compare_fn(lhs, rhs)) } @@ -201,9 +201,9 @@ impl ResolvableOwned for UntypedFloatFallback { } } -impl ResolvableOwned for UntypedFloat { +impl ResolvableOwned for UntypedFloat { fn resolve_from_value( - value: OwnedFloatContent, + value: OwnedFloat, context: ResolutionContext, ) -> ExecutionResult { match value { From 8ee1dc208051dc816332290ecb9af788d040fbd3 Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 6 Jan 2026 03:17:54 +0100 Subject: [PATCH 26/58] fix: Fix style --- src/expressions/concepts/content.rs | 43 ++++++++++++++++++------- src/expressions/values/float.rs | 5 +-- src/expressions/values/float_untyped.rs | 5 +-- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index c5e1cf41..d9789c1e 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -31,7 +31,9 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { where Self: Sized, Self::Form: IsFormOf, - for<'l> Self::Type: IsHierarchicalType = >::Content<'l>>, + for<'l> Self::Type: IsHierarchicalType< + Content<'l, Self::Form> = >::Content<'l>, + >, Self::Form: IsHierarchicalForm, { U::downcast_from(self.into_content()) @@ -52,7 +54,9 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { ) -> Result, M::ShortCircuit<'a>> where Self: Sized, - Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Type: IsHierarchicalType< + Content<'a, Self::Form> = >::Content<'a>, + >, Self::Form: IsHierarchicalForm, { ::map_with::(self.into_content()) @@ -61,9 +65,12 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { fn into_referenceable(self) -> Actual<'a, Self::Type, BeReferenceable> where Self: Sized, - Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Type: IsHierarchicalType< + Content<'a, Self::Form> = >::Content<'a>, + >, Self::Form: IsHierarchicalForm, - for<'l> OwnedToReferenceableMapper: LeafMapper = Infallible>, + for<'l> OwnedToReferenceableMapper: + LeafMapper = Infallible>, { match self.map_with::() { Ok(output) => output, @@ -75,13 +82,17 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { impl<'a, C> Spanned where C: IntoValueContent<'a>, - for<'l> C::Type: IsHierarchicalType = >::Content<'l>>, + for<'l> C::Type: + IsHierarchicalType = >::Content<'l>>, C::Form: IsHierarchicalForm, { - pub(crate) fn downcast_resolve>(self, description: &str) -> ExecutionResult + pub(crate) fn downcast_resolve>( + self, + description: &str, + ) -> ExecutionResult where C::Form: IsFormOf<>::Type>, - >::Type: DowncastFrom + >::Type: DowncastFrom, { let Spanned(value, span_range) = self; let content = value.into_content(); @@ -114,7 +125,9 @@ where ) -> Result, M::ShortCircuit<'a>> where 'a: 'r, - Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Type: IsHierarchicalType< + Content<'a, Self::Form> = >::Content<'a>, + >, Self::Form: IsHierarchicalForm, { ::map_mut_with::(self) @@ -125,7 +138,9 @@ where ) -> Result, M::ShortCircuit<'a>> where 'a: 'r, - Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Type: IsHierarchicalType< + Content<'a, Self::Form> = >::Content<'a>, + >, Self::Form: IsHierarchicalForm, { ::map_ref_with::(self) @@ -135,7 +150,9 @@ where where // Bounds for map_mut_with to work 'a: 'r, - Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Type: IsHierarchicalType< + Content<'a, Self::Form> = >::Content<'a>, + >, Self::Form: IsHierarchicalForm, // Bounds for ToMutMapper to work @@ -152,7 +169,9 @@ where where // Bounds for map_mut_with to work 'a: 'r, - Self::Type: IsHierarchicalType = >::Content<'a>>, + Self::Type: IsHierarchicalType< + Content<'a, Self::Form> = >::Content<'a>, + >, Self::Form: IsHierarchicalForm, // Bounds for ToMutMapper to work @@ -201,4 +220,4 @@ where // .upcast::(); // F::into_returned_value(type_mapped) // } -// } \ No newline at end of file +// } diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index 0eb96d3a..c898fa19 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -123,10 +123,7 @@ impl Debug for OwnedFloat { /// Aligns types for comparison - converts untyped to match the other's type. /// Unlike integers, float conversion never fails (may lose precision). -fn align_types( - mut lhs: OwnedFloat, - mut rhs: OwnedFloat, -) -> (OwnedFloat, OwnedFloat) { +fn align_types(mut lhs: OwnedFloat, mut rhs: OwnedFloat) -> (OwnedFloat, OwnedFloat) { match (&lhs, &rhs) { (FloatContent::Untyped(l), typed) if !matches!(typed, FloatContent::Untyped(_)) => { lhs = l.into_kind(typed.kind()); diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 536cc1e3..143e5422 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -202,10 +202,7 @@ impl ResolvableOwned for UntypedFloatFallback { } impl ResolvableOwned for UntypedFloat { - fn resolve_from_value( - value: OwnedFloat, - context: ResolutionContext, - ) -> ExecutionResult { + fn resolve_from_value(value: OwnedFloat, context: ResolutionContext) -> ExecutionResult { match value { FloatContent::Untyped(value) => Ok(value), _ => context.err("an untyped float", value), From 3148029e8e5dc2bbe82b1f3d88df6b751dadddbd Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Jan 2026 02:37:45 +0000 Subject: [PATCH 27/58] refactor: Extract impl_value_content_traits sub-macro Extracted a new `impl_value_content_traits` macro that deduplicates the IsValueContent/IntoValueContent/FromValueContent implementations across `define_leaf_type`, `define_parent_type`, and `define_dyn_type`. The macro generates all form variants for each type kind: - `leaf:` - implements for X, &X, &mut X, plus BeMutable, BeShared, BeAnyRef, BeAnyMut, BeReferenceable wrappers - `parent:` - implements generic impl for $content<'a, F> - `dyn:` - implements for Box, &D, &mut D, plus BeMutable, BeShared, BeAnyRef, BeAnyMut wrappers (BeReferenceable doesn't work for unsized) This refactoring deduplicates existing code and adds previously missing form implementations. --- src/expressions/concepts/type_traits.rs | 312 ++++++++++++++++++++---- 1 file changed, 264 insertions(+), 48 deletions(-) diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 6694b92c..84e18226 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -411,22 +411,7 @@ macro_rules! define_parent_type { $( >::Content<'a>: Copy ),* {} - impl<'a, F: IsHierarchicalForm> IsValueContent<'a> for $content<'a, F> { - type Type = $type_def; - type Form = F; - } - - impl<'a, F: IsHierarchicalForm> IntoValueContent<'a> for $content<'a, F> { - fn into_content(self) -> Self { - self - } - } - - impl<'a, F: IsHierarchicalForm> FromValueContent<'a> for $content<'a, F> { - fn from_content(content: Self) -> Self { - content - } - } + impl_value_content_traits!(parent: $type_def, $content); impl<'a, F: IsHierarchicalForm> HasLeafKind for $content<'a, F> { type LeafKind = $leaf_kind; @@ -621,86 +606,320 @@ macro_rules! define_leaf_type { } } + impl_value_content_traits!(leaf: $type_def, $content_type); + + impl IsValueLeaf for $content_type {} + + $( + impl $dyn_trait for $content_type { + $($dyn_trait_impl)* + } + impl CastDyn for $content_type { + fn map_boxed(self: Box) -> Option> { + Some(self) + } + fn map_ref(&self) -> Option<&dyn $dyn_trait> { + Some(self) + } + fn map_mut(&mut self) -> Option<&mut dyn $dyn_trait> { + Some(self) + } + } + )* + impl_fallback_is_iterable!({$($dyn_trait)*} for $content_type); + + impl_ancestor_chain_conversions!( + $type_def => $parent($parent_content :: $parent_variant) $(=> $ancestor)* + ); + }; +} + +pub(crate) use define_leaf_type; + +pub(crate) struct DynMapper(std::marker::PhantomData); + +/// Implements `IsValueContent`, `IntoValueContent`, and `FromValueContent` for various +/// form wrappers of a content type. This macro deduplicates code across `define_leaf_type`, +/// `define_parent_type`, and `define_dyn_type`. +macro_rules! impl_value_content_traits { + // For leaf types - implements for all common form wrappers + (leaf: $type_def:ty, $content_type:ty) => { + // BeOwned: content is X impl<'a> IsValueContent<'a> for $content_type { type Type = $type_def; type Form = BeOwned; } - impl<'a> IntoValueContent<'a> for $content_type { fn into_content(self) -> Self { self } } - impl<'a> FromValueContent<'a> for $content_type { fn from_content(content: Self) -> Self { content } } + // BeRef: content is &'a X impl<'a> IsValueContent<'a> for &'a $content_type { type Type = $type_def; type Form = BeRef; } - impl<'a> IntoValueContent<'a> for &'a $content_type { fn into_content(self) -> Self { self } } - impl<'a> FromValueContent<'a> for &'a $content_type { fn from_content(content: Self) -> Self { content } } + // BeMut: content is &'a mut X impl<'a> IsValueContent<'a> for &'a mut $content_type { type Type = $type_def; type Form = BeMut; } - impl<'a> IntoValueContent<'a> for &'a mut $content_type { fn into_content(self) -> Self { self } } - impl<'a> FromValueContent<'a> for &'a mut $content_type { fn from_content(content: Self) -> Self { content } } - impl IsValueLeaf for $content_type {} + // BeMutable: content is MutableSubRcRefCell + impl<'a> IsValueContent<'a> for MutableSubRcRefCell { + type Type = $type_def; + type Form = BeMutable; + } + impl<'a> IntoValueContent<'a> for MutableSubRcRefCell { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for MutableSubRcRefCell { + fn from_content(content: Self) -> Self { + content + } + } - $( - impl $dyn_trait for $content_type { - $($dyn_trait_impl)* + // BeShared: content is SharedSubRcRefCell + impl<'a> IsValueContent<'a> for SharedSubRcRefCell { + type Type = $type_def; + type Form = BeShared; + } + impl<'a> IntoValueContent<'a> for SharedSubRcRefCell { + fn into_content(self) -> Self { + self } - impl CastDyn for $content_type { - fn map_boxed(self: Box) -> Option> { - Some(self) - } - fn map_ref(&self) -> Option<&dyn $dyn_trait> { - Some(self) - } - fn map_mut(&mut self) -> Option<&mut dyn $dyn_trait> { - Some(self) - } + } + impl<'a> FromValueContent<'a> for SharedSubRcRefCell { + fn from_content(content: Self) -> Self { + content } - )* - impl_fallback_is_iterable!({$($dyn_trait)*} for $content_type); + } - impl_ancestor_chain_conversions!( - $type_def => $parent($parent_content :: $parent_variant) $(=> $ancestor)* - ); + // BeAnyRef: content is AnyRef<'a, X> + impl<'a> IsValueContent<'a> for AnyRef<'a, $content_type> { + type Type = $type_def; + type Form = BeAnyRef; + } + impl<'a> IntoValueContent<'a> for AnyRef<'a, $content_type> { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for AnyRef<'a, $content_type> { + fn from_content(content: Self) -> Self { + content + } + } + + // BeAnyMut: content is AnyMut<'a, X> + impl<'a> IsValueContent<'a> for AnyMut<'a, $content_type> { + type Type = $type_def; + type Form = BeAnyMut; + } + impl<'a> IntoValueContent<'a> for AnyMut<'a, $content_type> { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for AnyMut<'a, $content_type> { + fn from_content(content: Self) -> Self { + content + } + } + + // BeReferenceable: content is Rc> + impl<'a> IsValueContent<'a> for Rc> { + type Type = $type_def; + type Form = BeReferenceable; + } + impl<'a> IntoValueContent<'a> for Rc> { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for Rc> { + fn from_content(content: Self) -> Self { + content + } + } + + // Note: BeAssignee uses the same Leaf type as BeMutable (MutableSubRcRefCell), + // so we can't have a separate impl for it - the BeMutable impl covers both. }; -} -pub(crate) use define_leaf_type; + // For parent types - $content<'a, F> where F: IsHierarchicalForm + (parent: $type_def:ty, $content:ident) => { + impl<'a, F: IsHierarchicalForm> IsValueContent<'a> for $content<'a, F> { + type Type = $type_def; + type Form = F; + } + impl<'a, F: IsHierarchicalForm> IntoValueContent<'a> for $content<'a, F> { + fn into_content(self) -> Self { + self + } + } + impl<'a, F: IsHierarchicalForm> FromValueContent<'a> for $content<'a, F> { + fn from_content(content: Self) -> Self { + content + } + } + }; -pub(crate) struct DynMapper(std::marker::PhantomData); + // For dyn types - implements for all dyn-compatible form wrappers + (dyn: $type_def:ty, $dyn_type:ty) => { + // The unsized dyn type itself needs IsValueContent for IsDynLeaf requirements + impl<'a> IsValueContent<'a> for $dyn_type { + type Type = $type_def; + type Form = BeOwned; + } + + // BeOwned: content is Box + impl<'a> IsValueContent<'a> for Box<$dyn_type> { + type Type = $type_def; + type Form = BeOwned; + } + impl<'a> IntoValueContent<'a> for Box<$dyn_type> { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for Box<$dyn_type> { + fn from_content(content: Self) -> Self { + content + } + } + + // BeRef: content is &'a D + impl<'a> IsValueContent<'a> for &'a $dyn_type { + type Type = $type_def; + type Form = BeRef; + } + impl<'a> IntoValueContent<'a> for &'a $dyn_type { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for &'a $dyn_type { + fn from_content(content: Self) -> Self { + content + } + } + + // BeMut: content is &'a mut D + impl<'a> IsValueContent<'a> for &'a mut $dyn_type { + type Type = $type_def; + type Form = BeMut; + } + impl<'a> IntoValueContent<'a> for &'a mut $dyn_type { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for &'a mut $dyn_type { + fn from_content(content: Self) -> Self { + content + } + } + + // BeMutable: content is MutableSubRcRefCell + impl<'a> IsValueContent<'a> for MutableSubRcRefCell { + type Type = $type_def; + type Form = BeMutable; + } + impl<'a> IntoValueContent<'a> for MutableSubRcRefCell { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for MutableSubRcRefCell { + fn from_content(content: Self) -> Self { + content + } + } + + // BeShared: content is SharedSubRcRefCell + impl<'a> IsValueContent<'a> for SharedSubRcRefCell { + type Type = $type_def; + type Form = BeShared; + } + impl<'a> IntoValueContent<'a> for SharedSubRcRefCell { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for SharedSubRcRefCell { + fn from_content(content: Self) -> Self { + content + } + } + + // BeAnyRef: content is AnyRef<'a, D> + impl<'a> IsValueContent<'a> for AnyRef<'a, $dyn_type> { + type Type = $type_def; + type Form = BeAnyRef; + } + impl<'a> IntoValueContent<'a> for AnyRef<'a, $dyn_type> { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for AnyRef<'a, $dyn_type> { + fn from_content(content: Self) -> Self { + content + } + } + + // BeAnyMut: content is AnyMut<'a, D> + impl<'a> IsValueContent<'a> for AnyMut<'a, $dyn_type> { + type Type = $type_def; + type Form = BeAnyMut; + } + impl<'a> IntoValueContent<'a> for AnyMut<'a, $dyn_type> { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for AnyMut<'a, $dyn_type> { + fn from_content(content: Self) -> Self { + content + } + } + + // Note: BeAssignee uses the same DynLeaf type as BeMutable, so covered above. + // Note: BeReferenceable (Rc>) doesn't work for unsized D. + }; +} + +pub(crate) use impl_value_content_traits; macro_rules! define_dyn_type { ( @@ -744,10 +963,7 @@ macro_rules! define_dyn_type { impl TypeFeatureResolver for $type_def: [$type_def] } - impl<'a> IsValueContent<'a> for $dyn_type { - type Type = $type_def; - type Form = BeOwned; - } + impl_value_content_traits!(dyn: $type_def, $dyn_type); impl IsDynLeaf for $dyn_type {} From b363b40f4d7dbfafb19df49ec1c5b78217792518 Mon Sep 17 00:00:00 2001 From: David Edey Date: Wed, 7 Jan 2026 22:31:11 +0100 Subject: [PATCH 28/58] feat: Add Assignee leaf type --- plans/TODO.md | 4 ++-- src/expressions/concepts/forms/assignee.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index edc3ac8e..f857c028 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -238,7 +238,7 @@ First, read the @./2025-11-vision.md - [ ] ... and a `ReduceMapper::::map(content, |x| -> y)` - [ ] ... Probably remove e.g. `map_with` etc on actual? - [x] Trial getting rid of `Actual` completely? - - [ ] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` + - [x] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` - [x] Create new branch - [x] Resolve issue with `2.3` not resolving into `2f32` any more - [ ] .. same for int... @@ -248,7 +248,7 @@ First, read the @./2025-11-vision.md - [ ] `type ValueMut = QqqMut` - [ ] ... and move methods - [ ] Rename `ValueType` to `AnyType` and `Value` to `AnyValue` - - [ ] Replace `Owned` as `QqqOwned` + - [ ] Replace `Owned` with `QqqOwned` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` - [ ] And similarly for other values... - [ ] Stage 2 of the form migration: diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 807f8421..16b43788 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -1,36 +1,36 @@ use super::*; -type QqqAssignee = Actual<'static, T, BeAssignee>; +pub(crate) struct QqqAssignee(pub(crate) MutableSubRcRefCell); #[derive(Copy, Clone)] pub(crate) struct BeAssignee; impl IsForm for BeAssignee {} impl IsHierarchicalForm for BeAssignee { - type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; + type Leaf<'a, T: IsValueLeaf> = QqqAssignee; } impl IsDynCompatibleForm for BeAssignee { - type DynLeaf<'a, T: 'static + ?Sized> = MutableSubRcRefCell; + type DynLeaf<'a, T: 'static + ?Sized> = QqqAssignee; } impl IsDynMappableForm for BeAssignee { fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> { - leaf.map_optional(T::map_mut) + leaf.0.map_optional(T::map_mut).map(QqqAssignee) } } impl LeafAsRefForm for BeAssignee { fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { - leaf + &leaf.0 } } impl LeafAsMutForm for BeAssignee { fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { - leaf + &mut leaf.0 } } From 40efdba1ac1c401fee4db45bfc3c1f6e37d51d70 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Jan 2026 21:37:36 +0000 Subject: [PATCH 29/58] feat: Add BeAssignee impls to impl_value_content_traits macro Now that BeAssignee has its own wrapper type (QqqAssignee), add the IsValueContent/IntoValueContent/FromValueContent implementations for both leaf and dyn type variants. --- src/expressions/concepts/type_traits.rs | 34 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 84e18226..6786236d 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -772,8 +772,21 @@ macro_rules! impl_value_content_traits { } } - // Note: BeAssignee uses the same Leaf type as BeMutable (MutableSubRcRefCell), - // so we can't have a separate impl for it - the BeMutable impl covers both. + // BeAssignee: content is QqqAssignee + impl<'a> IsValueContent<'a> for QqqAssignee<$content_type> { + type Type = $type_def; + type Form = BeAssignee; + } + impl<'a> IntoValueContent<'a> for QqqAssignee<$content_type> { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for QqqAssignee<$content_type> { + fn from_content(content: Self) -> Self { + content + } + } }; // For parent types - $content<'a, F> where F: IsHierarchicalForm @@ -914,7 +927,22 @@ macro_rules! impl_value_content_traits { } } - // Note: BeAssignee uses the same DynLeaf type as BeMutable, so covered above. + // BeAssignee: content is QqqAssignee + impl<'a> IsValueContent<'a> for QqqAssignee<$dyn_type> { + type Type = $type_def; + type Form = BeAssignee; + } + impl<'a> IntoValueContent<'a> for QqqAssignee<$dyn_type> { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for QqqAssignee<$dyn_type> { + fn from_content(content: Self) -> Self { + content + } + } + // Note: BeReferenceable (Rc>) doesn't work for unsized D. }; } From 85ebdf6ba3c7d22b9d4fcea76ee85b8271a57553 Mon Sep 17 00:00:00 2001 From: David Edey Date: Wed, 7 Jan 2026 22:51:27 +0100 Subject: [PATCH 30/58] refactor: Remove Owned wrapper from FloatValue --- plans/TODO.md | 10 ++-- src/expressions/values/float.rs | 66 +++++++++++----------- src/expressions/values/float_subtypes.rs | 10 ++-- src/expressions/values/float_untyped.rs | 12 ++-- src/expressions/values/integer.rs | 60 ++++++++++---------- src/expressions/values/integer_subtypes.rs | 6 +- src/expressions/values/integer_untyped.rs | 8 +-- src/expressions/values/parser.rs | 4 +- src/expressions/values/range.rs | 2 +- src/expressions/values/value.rs | 2 +- 10 files changed, 90 insertions(+), 90 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index f857c028..4acbc785 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -241,13 +241,13 @@ First, read the @./2025-11-vision.md - [x] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` - [x] Create new branch - [x] Resolve issue with `2.3` not resolving into `2f32` any more - - [ ] .. same for int... + - [x] .. same for int... - [ ] Change `type Value<'a, F> = ValueContent<'a, F>` and `type OwnedValue` with: - - [ ] `type OwnedValue = QqqOwned` - - [ ] `type ValueRef = QqqRef` - - [ ] `type ValueMut = QqqMut` + - [ ] `ValueType` => `AnyType` + - [ ] `type AnyValue = QqqOwned` + - [ ] `type AnyValueRef = QqqRef` + - [ ] `type AnyValueMut = QqqMut` - [ ] ... and move methods - - [ ] Rename `ValueType` to `AnyType` and `Value` to `AnyValue` - [ ] Replace `Owned` with `QqqOwned` - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` - [ ] And similarly for other values... diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index c898fa19..1a44325d 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -14,10 +14,10 @@ define_parent_type! { articled_display_name: "a float", } -pub(crate) type OwnedFloat = FloatContent<'static, BeOwned>; -pub(crate) type FloatRef<'a> = FloatContent<'a, BeRef>; +pub(crate) type FloatValue = FloatContent<'static, BeOwned>; +pub(crate) type FloatValueRef<'a> = FloatContent<'a, BeRef>; -impl OwnedFloat { +impl FloatValue { pub(super) fn for_litfloat(lit: &syn::LitFloat) -> ParseResult { Ok(match lit.suffix() { "" => FloatContent::Untyped(UntypedFloat::new_from_lit_float(lit)?), @@ -31,9 +31,9 @@ impl OwnedFloat { }) } - pub(crate) fn resolve_untyped_to_match(self, target: FloatRef) -> OwnedFloat { + pub(crate) fn resolve_untyped_to_match(self, target: FloatValueRef) -> FloatValue { match self { - FloatContent::Untyped(this) => this.into_owned().into_kind(target.kind()), + FloatContent::Untyped(this) => this.into_kind(target.kind()), other => other, } } @@ -53,7 +53,7 @@ impl OwnedFloat { } // TODO[concepts]: Move to FloatRef<'a> when we can -impl OwnedFloat { +impl FloatValue { /// Outputs this float value to a token stream. /// For finite values, outputs a literal. For non-finite values (infinity, NaN), /// outputs the equivalent constant path like `f32::INFINITY`. @@ -100,10 +100,10 @@ impl OwnedFloat { } fn assign_op( - mut left: Assignee, + mut left: Assignee, right: R, context: BinaryOperationCallContext, - op: fn(BinaryOperationCallContext, OwnedFloat, R) -> ExecutionResult, + op: fn(BinaryOperationCallContext, FloatValue, R) -> ExecutionResult, ) -> ExecutionResult<()> { let left_value = core::mem::replace(&mut *left, FloatContent::F32(0.0)); let result = op(context, left_value, right)?; @@ -111,7 +111,7 @@ fn assign_op( Ok(()) } -impl Debug for OwnedFloat { +impl Debug for FloatValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { FloatContent::Untyped(v) => write!(f, "{}", v.into_fallback()), @@ -123,7 +123,7 @@ impl Debug for OwnedFloat { /// Aligns types for comparison - converts untyped to match the other's type. /// Unlike integers, float conversion never fails (may lose precision). -fn align_types(mut lhs: OwnedFloat, mut rhs: OwnedFloat) -> (OwnedFloat, OwnedFloat) { +fn align_types(mut lhs: FloatValue, mut rhs: FloatValue) -> (FloatValue, FloatValue) { match (&lhs, &rhs) { (FloatContent::Untyped(l), typed) if !matches!(typed, FloatContent::Untyped(_)) => { lhs = l.into_kind(typed.kind()); @@ -137,7 +137,7 @@ fn align_types(mut lhs: OwnedFloat, mut rhs: OwnedFloat) -> (OwnedFloat, OwnedFl } // TODO[concepts]: Should really be over FloatRef<'a> -impl ValuesEqual for OwnedFloat { +impl ValuesEqual for FloatValue { /// Handles type coercion between typed and untyped floats. /// Uses Rust's float `==`, so `NaN != NaN`. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { @@ -170,7 +170,7 @@ define_type_features! { impl FloatType, pub(crate) mod float_interface { pub(crate) mod methods { - fn is_nan(this: OwnedFloat) -> bool { + fn is_nan(this: FloatValue) -> bool { match this { FloatContent::Untyped(x) => x.into_fallback().is_nan(), FloatContent::F32(x) => x.is_nan(), @@ -178,7 +178,7 @@ define_type_features! { } } - fn is_infinite(this: OwnedFloat) -> bool { + fn is_infinite(this: FloatValue) -> bool { match this { FloatContent::Untyped(x) => x.into_fallback().is_infinite(), FloatContent::F32(x) => x.is_infinite(), @@ -186,7 +186,7 @@ define_type_features! { } } - fn is_finite(this: OwnedFloat) -> bool { + fn is_finite(this: FloatValue) -> bool { match this { FloatContent::Untyped(x) => x.into_fallback().is_finite(), FloatContent::F32(x) => x.is_finite(), @@ -194,7 +194,7 @@ define_type_features! { } } - fn is_sign_positive(this: OwnedFloat) -> bool { + fn is_sign_positive(this: FloatValue) -> bool { match this { FloatContent::Untyped(x) => x.into_fallback().is_sign_positive(), FloatContent::F32(x) => x.is_sign_positive(), @@ -202,7 +202,7 @@ define_type_features! { } } - fn is_sign_negative(this: OwnedFloat) -> bool { + fn is_sign_negative(this: FloatValue) -> bool { match this { FloatContent::Untyped(x) => x.into_fallback().is_sign_negative(), FloatContent::F32(x) => x.is_sign_negative(), @@ -213,7 +213,7 @@ define_type_features! { pub(crate) mod unary_operations { } pub(crate) mod binary_operations { - fn add(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn add(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a + b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a + b), @@ -221,11 +221,11 @@ define_type_features! { } } - [context] fn add_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn add_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, add) } - fn sub(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn sub(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a - b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a - b), @@ -233,11 +233,11 @@ define_type_features! { } } - [context] fn sub_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn sub_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, sub) } - fn mul(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn mul(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a * b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a * b), @@ -245,11 +245,11 @@ define_type_features! { } } - [context] fn mul_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn mul_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, mul) } - fn div(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn div(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a / b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a / b), @@ -257,11 +257,11 @@ define_type_features! { } } - [context] fn div_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn div_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, div) } - fn rem(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn rem(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a % b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a % b), @@ -269,11 +269,11 @@ define_type_features! { } } - [context] fn rem_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn rem_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { assign_op(left, right, context, rem) } - fn lt(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn lt(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a < b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a < b), @@ -281,7 +281,7 @@ define_type_features! { } } - fn le(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn le(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a <= b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a <= b), @@ -289,7 +289,7 @@ define_type_features! { } } - fn gt(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn gt(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a > b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a > b), @@ -297,7 +297,7 @@ define_type_features! { } } - fn ge(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn ge(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a >= b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a >= b), @@ -305,7 +305,7 @@ define_type_features! { } } - fn eq(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn eq(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a == b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a == b), @@ -313,7 +313,7 @@ define_type_features! { } } - fn ne(left: OwnedFloat, right: Spanned) -> ExecutionResult { + fn ne(left: FloatValue, right: Spanned) -> ExecutionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a != b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a != b), @@ -354,7 +354,7 @@ define_type_features! { impl_resolvable_argument_for! { FloatType, - (value, context) -> OwnedFloat { + (value, context) -> FloatValue { match value { Value::Float(value) => Ok(value), other => context.err("a float", other), diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 8458bc52..71431ede 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -170,12 +170,12 @@ macro_rules! impl_resolvable_float_subtype { impl ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { let span = self.span_range(); - let float_value: OwnedFloat = self.resolve_as(resolution_target)?; + let float_value: FloatValue = self.resolve_as(resolution_target)?; Spanned(float_value, span).resolve_as(resolution_target) } } - impl ResolveAs> for Spanned { + impl ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { let Spanned(value, span) = self; match value { @@ -195,15 +195,15 @@ macro_rules! impl_resolvable_float_subtype { type ValueType = $type_def; } - impl From<$type> for OwnedFloat { + impl From<$type> for FloatValue { fn from(value: $type) -> Self { FloatContent::$variant(value) } } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - value: OwnedFloat, + value: FloatValue, context: ResolutionContext, ) -> ExecutionResult { match value { diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 143e5422..fa34b6f8 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -26,7 +26,7 @@ impl UntypedFloat { /// Converts an untyped float to a specific float kind. /// Unlike integers, float conversion never fails (may lose precision). - pub(crate) fn into_kind(self, kind: FloatLeafKind) -> OwnedFloat { + pub(crate) fn into_kind(self, kind: FloatLeafKind) -> FloatValue { match kind { FloatLeafKind::Untyped(_) => FloatContent::Untyped(self), FloatLeafKind::F32(_) => FloatContent::F32(self.0 as f32), @@ -36,9 +36,9 @@ impl UntypedFloat { pub(crate) fn paired_operation( self, - rhs: Spanned, + rhs: Spanned, perform_fn: fn(FallbackFloat, FallbackFloat) -> FallbackFloat, - ) -> ExecutionResult { + ) -> ExecutionResult { let lhs = self.0; let rhs: UntypedFloat = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; @@ -48,7 +48,7 @@ impl UntypedFloat { pub(crate) fn paired_comparison( self, - rhs: Spanned, + rhs: Spanned, compare_fn: fn(FallbackFloat, FallbackFloat) -> bool, ) -> ExecutionResult { let lhs = self.0; @@ -201,8 +201,8 @@ impl ResolvableOwned for UntypedFloatFallback { } } -impl ResolvableOwned for UntypedFloat { - fn resolve_from_value(value: OwnedFloat, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for UntypedFloat { + fn resolve_from_value(value: FloatValue, context: ResolutionContext) -> ExecutionResult { match value { FloatContent::Untyped(value) => Ok(value), _ => context.err("an untyped float", value), diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index 769963ea..5bcc3699 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -56,7 +56,7 @@ impl IntegerValue { } pub(crate) fn resolve_untyped_to_match_other( - Spanned(Owned(value), span): Spanned>, + Spanned(value, span): Spanned, other: &Value, ) -> ExecutionResult { match (value, other) { @@ -68,7 +68,7 @@ impl IntegerValue { } pub(crate) fn resolve_untyped_to_match( - Spanned(Owned(value), span): Spanned>, + Spanned(value, span): Spanned, target: &IntegerValue, ) -> ExecutionResult { match value { @@ -83,12 +83,12 @@ impl IntegerValue { context: BinaryOperationCallContext, op: fn( BinaryOperationCallContext, - Spanned>, + Spanned, R, ) -> ExecutionResult, ) -> ExecutionResult<()> { let left_value = core::mem::replace(&mut *left, IntegerValue::U32(0)); - let result = op(context, Spanned(Owned(left_value), left_span), right)?; + let result = op(context, Spanned(left_value, left_span), right)?; *left = result; Ok(()) } @@ -208,7 +208,7 @@ define_type_features! { pub(crate) mod unary_operations { } pub(crate) mod binary_operations { - [context] fn add(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn add(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_add), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_add), @@ -226,11 +226,11 @@ define_type_features! { } } - [context] fn add_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn add_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, add) } - [context] fn sub(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn sub(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_sub), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_sub), @@ -248,11 +248,11 @@ define_type_features! { } } - [context] fn sub_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn sub_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, sub) } - [context] fn mul(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn mul(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_mul), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_mul), @@ -270,11 +270,11 @@ define_type_features! { } } - [context] fn mul_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn mul_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, mul) } - [context] fn div(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn div(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_div), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_div), @@ -292,11 +292,11 @@ define_type_features! { } } - [context] fn div_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn div_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, div) } - [context] fn rem(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn rem(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_rem), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_rem), @@ -314,11 +314,11 @@ define_type_features! { } } - [context] fn rem_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn rem_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, rem) } - [context] fn bitxor(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn bitxor(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, |a, b| Some(a ^ b)), IntegerValue::U8(left) => left.paired_operation(right, context, |a, b| Some(a ^ b)), @@ -336,11 +336,11 @@ define_type_features! { } } - [context] fn bitxor_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn bitxor_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitxor) } - [context] fn bitand(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn bitand(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, |a, b| Some(a & b)), IntegerValue::U8(left) => left.paired_operation(right, context, |a, b| Some(a & b)), @@ -358,11 +358,11 @@ define_type_features! { } } - [context] fn bitand_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn bitand_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitand) } - [context] fn bitor(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn bitor(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, |a, b| Some(a | b)), IntegerValue::U8(left) => left.paired_operation(right, context, |a, b| Some(a | b)), @@ -380,12 +380,12 @@ define_type_features! { } } - [context] fn bitor_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn bitor_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitor) } - [context] fn shift_left(lhs: Spanned>, CoercedToU32(right): CoercedToU32) -> ExecutionResult { - match lhs.0.into_inner() { + [context] fn shift_left(lhs: Spanned, CoercedToU32(right): CoercedToU32) -> ExecutionResult { + match lhs.0 { IntegerValue::Untyped(left) => left.shift_operation(right, context, FallbackInteger::checked_shl), IntegerValue::U8(left) => left.shift_operation(right, context, u8::checked_shl), IntegerValue::U16(left) => left.shift_operation(right, context, u16::checked_shl), @@ -406,8 +406,8 @@ define_type_features! { IntegerValue::assign_op(lhs, rhs, context, shift_left) } - [context] fn shift_right(lhs: Spanned>, CoercedToU32(right): CoercedToU32) -> ExecutionResult { - match lhs.0.into_inner() { + [context] fn shift_right(lhs: Spanned, CoercedToU32(right): CoercedToU32) -> ExecutionResult { + match lhs.0 { IntegerValue::Untyped(left) => left.shift_operation(right, context, FallbackInteger::checked_shr), IntegerValue::U8(left) => left.shift_operation(right, context, u8::checked_shr), IntegerValue::U16(left) => left.shift_operation(right, context, u16::checked_shr), @@ -428,7 +428,7 @@ define_type_features! { IntegerValue::assign_op(lhs, rhs, context, shift_right) } - fn lt(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn lt(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a < b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a < b), @@ -446,7 +446,7 @@ define_type_features! { } } - fn le(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn le(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a <= b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a <= b), @@ -464,7 +464,7 @@ define_type_features! { } } - fn gt(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn gt(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a > b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a > b), @@ -482,7 +482,7 @@ define_type_features! { } } - fn ge(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn ge(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a >= b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a >= b), @@ -500,7 +500,7 @@ define_type_features! { } } - fn eq(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn eq(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a == b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a == b), @@ -518,7 +518,7 @@ define_type_features! { } } - fn ne(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn ne(left: Spanned, right: Spanned) -> ExecutionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a != b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a != b), diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 18578155..0fc1beb6 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -204,15 +204,15 @@ macro_rules! impl_resolvable_integer_subtype { impl ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { let span = self.span_range(); - let integer_value: Owned = self.resolve_as(resolution_target)?; + let integer_value: IntegerValue = self.resolve_as(resolution_target)?; Spanned(integer_value, span).resolve_as(resolution_target) } } - impl ResolveAs> for Spanned> { + impl ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { let Spanned(value, span) = self; - match value.0 { + match value { IntegerValue::Untyped(v) => Ok(OptionalSuffix(v.into_fallback() as $type)), IntegerValue::$variant(v) => Ok(OptionalSuffix(v)), v => span.type_err(format!( diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index 44351f43..e6892834 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -60,12 +60,12 @@ impl UntypedInteger { pub(crate) fn paired_operation( self, - rhs: Spanned>, + rhs: Spanned, context: BinaryOperationCallContext, perform_fn: fn(FallbackInteger, FallbackInteger) -> Option, ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedInteger = rhs.resolve_as("This operand")?; + let rhs: UntypedInteger = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; let output = perform_fn(lhs, rhs) .ok_or_else(|| UntypedInteger::binary_overflow_error(context, lhs, rhs))?; @@ -74,11 +74,11 @@ impl UntypedInteger { pub(crate) fn paired_comparison( self, - rhs: Spanned>, + rhs: Spanned, compare_fn: fn(FallbackInteger, FallbackInteger) -> bool, ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedInteger = rhs.resolve_as("This operand")?; + let rhs: UntypedInteger = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; Ok(compare_fn(lhs, rhs)) } diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 68b67623..6c9cecb8 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -249,9 +249,9 @@ define_type_features! { Ok(OutputStream::new_with(|s| s.push_tokens(float))) } - [context] fn float(this: Spanned>) -> ExecutionResult { + [context] fn float(this: Spanned>) -> ExecutionResult { let float: syn::LitFloat = parser(this, context)?.parse()?; - Ok(OwnedFloat::for_litfloat(&float)?) + Ok(FloatValue::for_litfloat(&float)?) } } pub(crate) mod unary_operations { diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index c7c5e04a..551c737e 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -440,7 +440,7 @@ impl IterableRangeOf { Value::Integer(mut start) => { if let Some(end) = &end { start = IntegerValue::resolve_untyped_to_match_other( - start.into_owned().spanned(dots.span_range()), + start.spanned(dots.span_range()), end, )?; } diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs index 221bde28..052fafdf 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/value.rs @@ -227,7 +227,7 @@ impl Value { Ok(int) => Some(int.into_owned_value()), Err(_) => None, }, - Lit::Float(lit) => match OwnedFloat::for_litfloat(lit) { + Lit::Float(lit) => match FloatValue::for_litfloat(lit) { Ok(float) => Some(float.into_owned_value()), Err(_) => None, }, From 3aaf25ef6091efc84ddf6979aa1e33e622a0f6b9 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 8 Jan 2026 01:51:42 +0100 Subject: [PATCH 31/58] refactor: Migrate value to AnyValue --- plans/TODO.md | 34 +- src/expressions/concepts/content.rs | 117 +++++- src/expressions/concepts/form.rs | 35 +- src/expressions/concepts/forms/any_mut.rs | 2 +- src/expressions/concepts/forms/any_ref.rs | 2 +- src/expressions/concepts/forms/argument.rs | 8 +- src/expressions/concepts/forms/assignee.rs | 4 +- .../concepts/forms/copy_on_write.rs | 2 +- src/expressions/concepts/forms/late_bound.rs | 4 +- src/expressions/concepts/forms/mutable.rs | 8 +- src/expressions/concepts/forms/owned.rs | 31 +- .../concepts/forms/referenceable.rs | 7 +- src/expressions/concepts/forms/shared.rs | 8 +- src/expressions/concepts/forms/simple_mut.rs | 1 + src/expressions/concepts/forms/simple_ref.rs | 11 +- src/expressions/concepts/type_traits.rs | 93 +++-- src/expressions/control_flow.rs | 9 +- src/expressions/equality.rs | 4 +- .../evaluation/assignment_frames.rs | 20 +- src/expressions/evaluation/evaluator.rs | 4 +- src/expressions/evaluation/node_conversion.rs | 2 +- src/expressions/evaluation/value_frames.rs | 33 +- src/expressions/expression_block.rs | 6 +- src/expressions/expression_parsing.rs | 2 +- src/expressions/operations.rs | 12 +- src/expressions/patterns.rs | 12 +- src/expressions/statements.rs | 2 +- src/expressions/type_resolution/arguments.rs | 80 +++-- src/expressions/type_resolution/outputs.rs | 4 +- src/expressions/type_resolution/type_data.rs | 4 +- src/expressions/type_resolution/type_kinds.rs | 74 +++- .../values/{value.rs => any_value.rs} | 339 +++++++++--------- src/expressions/values/array.rs | 49 +-- src/expressions/values/boolean.rs | 34 +- src/expressions/values/character.rs | 34 +- src/expressions/values/float.rs | 13 +- src/expressions/values/float_subtypes.rs | 56 +-- src/expressions/values/float_untyped.rs | 45 +-- src/expressions/values/integer.rs | 55 +-- src/expressions/values/integer_subtypes.rs | 58 +-- src/expressions/values/integer_untyped.rs | 47 +-- src/expressions/values/iterable.rs | 40 ++- src/expressions/values/iterator.rs | 39 +- src/expressions/values/mod.rs | 4 +- src/expressions/values/none.rs | 8 +- src/expressions/values/object.rs | 43 +-- src/expressions/values/parser.rs | 16 +- src/expressions/values/range.rs | 101 ++++-- src/expressions/values/stream.rs | 26 +- src/expressions/values/string.rs | 25 +- src/expressions/values/unsupported_literal.rs | 2 +- src/interpretation/bindings.rs | 53 +-- src/interpretation/interpreter.rs | 4 +- src/interpretation/output_stream.rs | 4 +- src/interpretation/refs.rs | 4 +- src/interpretation/variable.rs | 4 +- src/lib.rs | 4 +- src/misc/errors.rs | 2 +- src/misc/field_inputs.rs | 4 +- src/misc/iterators.rs | 23 +- src/misc/mod.rs | 2 +- 61 files changed, 1039 insertions(+), 734 deletions(-) rename src/expressions/values/{value.rs => any_value.rs} (65%) diff --git a/plans/TODO.md b/plans/TODO.md index 4acbc785..5836344f 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -210,7 +210,7 @@ First, read the @./2025-11-vision.md - [x] Generate value kinds from the macros - [x] Add (temporary) ability to link to TypeData and resolve methods from there - [x] Then implement all the macros - - [x] And use that to generate ValueLeafKind from the new macros + - [x] And use that to generate AnyValueLeafKind from the new macros - [x] Find a way to generate `from_source_name` - ideally efficiently - [x] Add ability to implement IsIterable - [x] `CastTarget` simply wraps `TypeKind` @@ -231,27 +231,26 @@ First, read the @./2025-11-vision.md - [x] Replace `FloatValue` with `type FloatValue = FloatContent<'static, BeOwned>` - [x] Replace `Value` with `type Value = ValueContent<'static, BeOwned>` - [x] Get rid of the `Actual` wrapper inside content - - [ ] --- - - [ ] Improve mappers: - - [ ] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - - [ ] 6 methods... `map_content`, `map_content_ref`, `map_content_mut` - - [ ] ... and a `ReduceMapper::::map(content, |x| -> y)` - - [ ] ... Probably remove e.g. `map_with` etc on actual? + // --- - [x] Trial getting rid of `Actual` completely? - [x] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` - [x] Create new branch - [x] Resolve issue with `2.3` not resolving into `2f32` any more - [x] .. same for int... - - [ ] Change `type Value<'a, F> = ValueContent<'a, F>` and `type OwnedValue` with: - - [ ] `ValueType` => `AnyType` - - [ ] `type AnyValue = QqqOwned` - - [ ] `type AnyValueRef = QqqRef` - - [ ] `type AnyValueMut = QqqMut` - - [ ] ... and move methods - - [ ] Replace `Owned` with `QqqOwned` - - [ ] Replace `Value`, `&Value` and `&mut Value` methods with methods on `Owned` / `Ref` / `Mut` - - [ ] And similarly for other values... + - [x] Update Value: + - [x] `ValueType` => `AnyType` + - [x] `type AnyValue = QqqOwned` + - [x] `type AnyValueRef = QqqRef` + - [x] `type AnyValueMut = QqqMut` + - [x] ... and move methods + - [ ] Remove `OwnedValue` + - [ ] Get rid of `Owned` - [ ] Stage 2 of the form migration: + - [ ] Improve mappers: + - [ ] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` + - [ ] 6 methods... `map_content`, `map_content_ref`, `map_content_mut` + - [ ] ... and a `ReduceMapper::::map(content, |x| -> y)` + - [ ] ... Probably remove e.g. `map_with` etc on actual? - [ ] Migrate `Shared`, `Mutable`, `Assignee` - [ ] Stage 3 - [ ] Migrate `CopyOnWrite` and relevant interconversions @@ -519,6 +518,9 @@ preinterpret::run! { - [ ] References store on them cached information - either up-front, via an `Rc>` or via a "resolve on first execute" - Value's relative offset from the top of the stack - An is last use flag +- [ ] Change the storage model for `OutputStream` + - [ ] Either just use `TokenStream` directly(!) (...and ignore Rust analyzer's poor handling of none groups) + - [ ] Or use an `Rc` model like https://github.com/dtolnay/proc-macro2/pull/341/files and Rust itself - Address `TODO[performance]` ## Deferred diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index d9789c1e..6b81c924 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -1,3 +1,5 @@ +use std::mem::transmute; + use super::*; /// Shorthand for representing the form F of a type T with a particular lifetime 'a. @@ -40,17 +42,18 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { } #[inline] - fn into_any(self) -> Actual<'a, ValueType, Self::Form> + fn into_any(self) -> Actual<'a, AnyType, Self::Form> where Self: Sized, - Self::Type: UpcastTo, - Self::Form: IsFormOf, + Self::Type: UpcastTo, + Self::Form: IsFormOf, { self.upcast() } fn map_with>( self, + mapper: M, ) -> Result, M::ShortCircuit<'a>> where Self: Sized, @@ -59,7 +62,7 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { >, Self::Form: IsHierarchicalForm, { - ::map_with::(self.into_content()) + ::map_with::(mapper, self.into_content()) } fn into_referenceable(self) -> Actual<'a, Self::Type, BeReferenceable> @@ -72,7 +75,7 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { for<'l> OwnedToReferenceableMapper: LeafMapper = Infallible>, { - match self.map_with::() { + match self.map_with(OwnedToReferenceableMapper) { Ok(output) => output, Err(infallible) => match infallible {}, // Need to include because of MSRV } @@ -100,15 +103,30 @@ where <>::Type>::resolve(content, span_range, description)?; Ok(X::from_content(resolved)) } + + pub(crate) fn downcast_resolve_spanned>( + self, + description: &str, + ) -> ExecutionResult> + where + C::Form: IsFormOf<>::Type>, + >::Type: DowncastFrom, + { + let span_range = self.1; + Ok(Spanned( + self.downcast_resolve::(description)?, + span_range, + )) + } } // TODO[concepts]: Remove eventually, along with IntoValue impl impl> IntoValue for X where - X::Type: UpcastTo, + X::Type: UpcastTo, BeOwned: IsFormOf, { - fn into_value(self) -> Value { + fn into_value(self) -> AnyValue { self.into_any() } } @@ -122,6 +140,7 @@ where { fn map_mut_with<'r, M: MutLeafMapper>( &'r mut self, + mapper: M, ) -> Result, M::ShortCircuit<'a>> where 'a: 'r, @@ -130,11 +149,12 @@ where >, Self::Form: IsHierarchicalForm, { - ::map_mut_with::(self) + ::map_mut_with::(mapper, self) } fn map_ref_with<'r, M: RefLeafMapper>( &'r self, + mapper: M, ) -> Result, M::ShortCircuit<'a>> where 'a: 'r, @@ -143,7 +163,7 @@ where >, Self::Form: IsHierarchicalForm, { - ::map_ref_with::(self) + ::map_ref_with::(mapper, self) } fn as_mut_value<'r>(&'r mut self) -> Actual<'r, Self::Type, BeMut> @@ -159,13 +179,34 @@ where Self::Form: LeafAsMutForm, BeMut: IsFormOf, { - match Self::map_mut_with::(self) { + match self.map_mut_with(ToMutMapper) { Ok(x) => x, Err(infallible) => match infallible {}, // Need to include because of MSRV } } fn as_ref_value<'r>(&'r self) -> Actual<'r, Self::Type, BeRef> + where + // Bounds for map_ref_with to work + 'a: 'r, + Self::Type: IsHierarchicalType< + Content<'a, Self::Form> = >::Content<'a>, + >, + Self::Form: IsHierarchicalForm, + + // Bounds for ToRefMapper to work + Self::Form: LeafAsRefForm, + { + match self.map_ref_with(ToRefMapper) { + Ok(x) => x, + Err(infallible) => match infallible {}, // Need to include because of MSRV + } + } + + /// This method should only be used when you are certain that the value should be cloned. + /// In most situations, you may wish to use [IsSelfValueContent::clone_to_owned_transparently] + /// instead. + fn clone_to_owned_infallible<'r>(&'r self) -> Actual<'static, Self::Type, BeOwned> where // Bounds for map_mut_with to work 'a: 'r, @@ -174,13 +215,63 @@ where >, Self::Form: IsHierarchicalForm, - // Bounds for ToMutMapper to work + // Bounds for LeafAsRefForm to work Self::Form: LeafAsRefForm, - BeMut: IsFormOf, + + // Bounds for cloning to work + Self: Sized, { - match Self::map_ref_with::(self) { + let mapped = match self.map_ref_with(ToOwnedInfallibleMapper) { Ok(x) => x, Err(infallible) => match infallible {}, // Need to include because of MSRV + }; + // SAFETY: All owned values don't make use of the lifetime parameter, + // so we can safely transmute to 'static here. + // I'd have liked to make this a where bound, but type resolution gets stuck in + // an infinite loop in that case. + unsafe { + transmute::, Actual<'static, Self::Type, BeOwned>>( + mapped, + ) + } + } + + /// A transparent clone is allowed for some types when doing method resolution. + /// * For these types, a &a can be transparently cloned into an owned a. + /// * For other types, an error is raised suggesting to use .clone() explicitly. + /// + /// See [TypeKind::supports_transparent_cloning] for more details. + fn clone_to_owned_transparently<'r>( + &'r self, + span_range: SpanRange, + ) -> ExecutionResult> + where + // Bounds for map_mut_with to work + 'a: 'r, + Self::Type: IsHierarchicalType< + Content<'a, Self::Form> = >::Content<'a>, + >, + Self::Form: IsHierarchicalForm, + + // Bounds for LeafAsRefForm to work + Self::Form: LeafAsRefForm, + + // Bounds for cloning to work + Self: Sized, + BeOwned: for<'l> IsFormOf = Self>, + { + let mapped = self.map_ref_with(ToOwnedTransparentlyMapper { span_range }); + // SAFETY: All owned values don't make use of the lifetime parameter, + // so we can safely transmute to 'static here. + // I'd have liked to make this a where bound, but type resolution gets stuck in + // an infinite loop in that case. + unsafe { + #[allow(clippy::useless_transmute)] + // Clippy thinks these types are identical but is wrong here + transmute::< + ExecutionResult>, + ExecutionResult>, + >(mapped) } } } diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 56279cbb..b8ec60b0 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -38,6 +38,27 @@ impl IsFormOfForKind(leaf: &'r Self::Leaf<'a, T>) -> &'r T; + + fn leaf_clone_to_owned_infallible<'r, 'a: 'r, T: IsValueLeaf>( + leaf: &'r Self::Leaf<'a, T>, + ) -> T { + Self::leaf_as_ref(leaf).clone() + } + + fn leaf_clone_to_owned_transparently<'r, 'a: 'r, T: IsValueLeaf>( + leaf: &'r Self::Leaf<'a, T>, + error_span: SpanRange, + ) -> ExecutionResult { + let type_kind = ::Type::type_kind(); + if type_kind.supports_transparent_cloning() { + Ok(Self::leaf_clone_to_owned_infallible(leaf)) + } else { + error_span.ownership_err(format!( + "An owned value is required, but a reference was received, and {} does not support transparent cloning. You may wish to use .clone() explicitly.", + type_kind.articled_display_name() + )) + } + } } pub(crate) trait LeafAsMutForm: IsHierarchicalForm { @@ -61,16 +82,14 @@ pub(crate) trait IsDynMappableForm: IsHierarchicalForm + IsDynCompatibleForm { ) -> Option>; } -pub(crate) trait MapFromArgument: IsFormOf { +pub(crate) trait MapFromArgument: IsFormOf { const ARGUMENT_OWNERSHIP: ArgumentOwnership; - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult>; + fn from_argument_value(value: ArgumentValue) + -> ExecutionResult>; } -pub(crate) trait MapIntoReturned: IsFormOf { - fn into_returned_value( - value: Actual<'static, ValueType, Self>, - ) -> ExecutionResult; +pub(crate) trait MapIntoReturned: IsFormOf { + fn into_returned_value(value: Actual<'static, AnyType, Self>) + -> ExecutionResult; } diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index d3172b72..32c750de 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -36,7 +36,7 @@ impl MapFromArgument for BeAnyMut { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // value.expect_mutable().as_any_mut() } diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 376c9624..123926d1 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -33,7 +33,7 @@ impl MapFromArgument for BeAnyRef { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // value.expect_shared().as_any_ref() } diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index b1116635..b3ce860a 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -15,7 +15,7 @@ impl MapFromArgument for BeArgument { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // Ok(value) } @@ -24,7 +24,7 @@ impl MapFromArgument for BeArgument { pub(crate) enum ArgumentContent { Owned(T), CopyOnWrite(CopyOnWriteContent), - Mutable(MutableSubRcRefCell), - Assignee(MutableSubRcRefCell), - Shared(SharedSubRcRefCell), + Mutable(MutableSubRcRefCell), + Assignee(MutableSubRcRefCell), + Shared(SharedSubRcRefCell), } diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 16b43788..40e0319d 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -1,6 +1,6 @@ use super::*; -pub(crate) struct QqqAssignee(pub(crate) MutableSubRcRefCell); +pub(crate) struct QqqAssignee(pub(crate) MutableSubRcRefCell); #[derive(Copy, Clone)] pub(crate) struct BeAssignee; @@ -40,7 +40,7 @@ impl MapFromArgument for BeAssignee { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // value.expect_assignee() } diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index f47ef8c1..be580afb 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -28,7 +28,7 @@ impl MapFromArgument for BeCopyOnWrite { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // value.expect_copy_on_write() } diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index 838e1035..a625ebc7 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -39,7 +39,7 @@ pub(crate) enum LateBoundContent { /// A copy-on-write value that can be converted to an owned value CopyOnWrite(CopyOnWriteContent), /// A mutable reference - Mutable(MutableSubRcRefCell), + Mutable(MutableSubRcRefCell), /// A shared reference where mutable access failed for a specific reason Shared(LateBoundShared), } @@ -51,6 +51,6 @@ pub(crate) struct LateBoundOwned { /// A shared value where mutable access failed for a specific reason pub(crate) struct LateBoundShared { - pub(crate) shared: SharedSubRcRefCell, + pub(crate) shared: SharedSubRcRefCell, pub(crate) reason_not_mutable: syn::Error, } diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index 7e50741f..909fce22 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -1,17 +1,17 @@ use super::*; -pub(crate) type QqqMutable = Actual<'static, T, BeMutable>; +pub(crate) type QqqMutable = MutableSubRcRefCell; #[derive(Copy, Clone)] pub(crate) struct BeMutable; impl IsForm for BeMutable {} impl IsHierarchicalForm for BeMutable { - type Leaf<'a, T: IsValueLeaf> = MutableSubRcRefCell; + type Leaf<'a, T: IsValueLeaf> = QqqMutable; } impl IsDynCompatibleForm for BeMutable { - type DynLeaf<'a, T: 'static + ?Sized> = MutableSubRcRefCell; + type DynLeaf<'a, T: 'static + ?Sized> = QqqMutable; } impl IsDynMappableForm for BeMutable { @@ -39,7 +39,7 @@ impl MapFromArgument for BeMutable { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { // value.expect_mutable() todo!() } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 9fd4a154..7485e42f 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -2,7 +2,7 @@ use super::*; pub(crate) type QqqOwned = Actual<'static, T, BeOwned>; -pub(crate) type QqqOwnedValue = Owned; +pub(crate) type QqqOwnedValue = Owned; /// Represents floating owned values. /// @@ -46,11 +46,38 @@ impl MapFromArgument for BeOwned { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { Ok(value.expect_owned().0) } } +pub(crate) struct ToOwnedInfallibleMapper; + +impl RefLeafMapper for ToOwnedInfallibleMapper { + type OutputForm = BeOwned; + type ShortCircuit<'a> = Infallible; + + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + self, + leaf: &'r F::Leaf<'a, L>, + ) -> Result { + Ok(F::leaf_clone_to_owned_infallible(leaf)) + } +} + +pub(crate) struct ToOwnedTransparentlyMapper { + pub(crate) span_range: SpanRange, +} + +impl RefLeafMapper for ToOwnedTransparentlyMapper { + type OutputForm = BeOwned; + type ShortCircuit<'a> = ExecutionInterrupt; + + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>(self, leaf: &'r F::Leaf<'a, L>) -> ExecutionResult { + F::leaf_clone_to_owned_transparently(leaf, self.span_range) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index fba3e6d3..001170ed 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -20,7 +20,7 @@ impl MapFromArgument for BeReferenceable { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { // Rc::new(RefCell::new(value.expect_owned())) todo!() } @@ -32,7 +32,10 @@ impl LeafMapper for OwnedToReferenceableMapper { type OutputForm = BeReferenceable; type ShortCircuit<'a> = Infallible; - fn map_leaf<'a, L: IsValueLeaf>(leaf: L) -> Result>, Self::ShortCircuit<'a>> { + fn map_leaf<'a, L: IsValueLeaf>( + self, + leaf: L, + ) -> Result>, Self::ShortCircuit<'a>> { Ok(Rc::new(RefCell::new(leaf))) } } diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 885f43c0..78b37bf1 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -1,17 +1,17 @@ use super::*; -pub(crate) type QqqShared = Actual<'static, T, BeShared>; +pub(crate) type QqqShared = SharedSubRcRefCell; #[derive(Copy, Clone)] pub(crate) struct BeShared; impl IsForm for BeShared {} impl IsHierarchicalForm for BeShared { - type Leaf<'a, T: IsValueLeaf> = SharedSubRcRefCell; + type Leaf<'a, T: IsValueLeaf> = QqqShared; } impl IsDynCompatibleForm for BeShared { - type DynLeaf<'a, T: 'static + ?Sized> = SharedSubRcRefCell; + type DynLeaf<'a, T: 'static + ?Sized> = QqqShared; } impl IsDynMappableForm for BeShared { @@ -33,7 +33,7 @@ impl MapFromArgument for BeShared { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { // value.expect_shared() todo!() } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index 9bb7a3ce..ed46c4e7 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -38,6 +38,7 @@ impl MutLeafMapper for ToMutMapper { type ShortCircuit<'a> = Infallible; fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + self, leaf: &'r mut F::Leaf<'a, L>, ) -> Result<&'r mut L, Infallible> { Ok(F::leaf_as_mut(leaf)) diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 4628421d..0bc1fd02 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -25,13 +25,22 @@ impl IsDynMappableForm for BeRef { } } +impl LeafAsRefForm for BeRef { + fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + leaf + } +} + pub(crate) struct ToRefMapper; impl RefLeafMapper for ToRefMapper { type OutputForm = BeRef; type ShortCircuit<'a> = Infallible; - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>(leaf: &'r F::Leaf<'a, L>) -> Result<&'r L, Infallible> { + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + self, + leaf: &'r F::Leaf<'a, L>, + ) -> Result<&'r L, Infallible> { Ok(F::leaf_as_ref(leaf)) } } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 6786236d..2ca19e77 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -24,17 +24,20 @@ pub(crate) trait IsHierarchicalType: IsType { // So the following where clause can be added where needed to make types line up: // for<'l> T: IsHierarchicalType = >::Content<'l>>, type Content<'a, F: IsHierarchicalForm>; - type LeafKind: IsSpecificLeafKind; + type LeafKind: IsLeafKind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( + mapper: M, structure: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + mapper: M, structure: &'r Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + mapper: M, structure: &'r mut Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; @@ -56,6 +59,7 @@ pub(crate) trait LeafMapper { type ShortCircuit<'a>; fn map_leaf<'a, L: IsValueLeaf>( + self, leaf: F::Leaf<'a, L>, ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>>; } @@ -65,6 +69,7 @@ pub(crate) trait RefLeafMapper { type ShortCircuit<'a>; fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + self, leaf: &'r F::Leaf<'a, L>, ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; } @@ -74,6 +79,7 @@ pub(crate) trait MutLeafMapper { type ShortCircuit<'a>; fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + self, leaf: &'r mut F::Leaf<'a, L>, ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; } @@ -163,7 +169,7 @@ macro_rules! impl_type_feature_resolver { None } - fn resolve_type_property(&self, property_name: &str) -> Option { + fn resolve_type_property(&self, property_name: &str) -> Option { // Purposefully doesn't resolve parents, but TBC if this is right <$type_def as TypeData>::resolve_type_property(property_name) } @@ -255,7 +261,7 @@ macro_rules! impl_ancestor_chain_conversions { pub(crate) use impl_ancestor_chain_conversions; pub(crate) trait IsValueLeaf: - 'static + IntoValueContent<'static, Form = BeOwned> + CastDyn + 'static + IntoValueContent<'static, Form = BeOwned> + CastDyn + Clone { } @@ -279,11 +285,11 @@ pub(crate) trait CastDyn { } pub(crate) trait HasLeafKind { - type LeafKind: IsSpecificLeafKind; + type LeafKind: IsLeafKind; fn kind(&self) -> Self::LeafKind; - fn value_kind(&self) -> ValueLeafKind { + fn value_kind(&self) -> AnyValueLeafKind { self.kind().into() } @@ -360,26 +366,29 @@ macro_rules! define_parent_type { type LeafKind = $leaf_kind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( + mapper: M, content: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { Ok(match content { - $( $content::$variant(x) => $content::$variant(<$variant_type>::map_with::<'a, F, M>(x)?), )* + $( $content::$variant(x) => $content::$variant(<$variant_type>::map_with::<'a, F, M>(mapper, x)?), )* }) } fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + mapper: M, content: &'r Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { Ok(match content { - $( $content::$variant(x) => $content::$variant(<$variant_type>::map_ref_with::<'r, 'a, F, M>(x)?), )* + $( $content::$variant(x) => $content::$variant(<$variant_type>::map_ref_with::<'r, 'a, F, M>(mapper, x)?), )* }) } fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + mapper: M, content: &'r mut Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { Ok(match content { - $( $content::$variant(x) => $content::$variant(<$variant_type>::map_mut_with::<'r, 'a, F, M>(x)?), )* + $( $content::$variant(x) => $content::$variant(<$variant_type>::map_mut_with::<'r, 'a, F, M>(mapper, x)?), )* }) } @@ -429,6 +438,10 @@ macro_rules! define_parent_type { $type_kind_vis struct $type_kind; impl $type_kind { + pub(crate) fn articled_display_name(&self) -> &'static str { + $articled_display_name + } + pub(crate) fn source_type_name(&self) -> &'static str { $source_type_name } @@ -444,15 +457,15 @@ macro_rules! define_parent_type { } $( - impl From<$leaf_kind> for ValueLeafKind { + impl From<$leaf_kind> for AnyValueLeafKind { fn from(kind: $leaf_kind) -> Self { let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); - ValueLeafKind::from(as_parent_kind) + AnyValueLeafKind::from(as_parent_kind) } } )? - impl IsSpecificLeafKind for $leaf_kind { + impl IsLeafKind for $leaf_kind { fn source_type_name(&self) -> &'static str { match self { $( Self::$variant(x) => x.source_type_name(), )* @@ -517,7 +530,7 @@ macro_rules! define_leaf_type { const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; fn type_kind() -> TypeKind { - TypeKind::Leaf(ValueLeafKind::from($kind)) + TypeKind::Leaf(AnyValueLeafKind::from($kind)) } // This is called for every leaf in a row as part of parsing @@ -538,21 +551,24 @@ macro_rules! define_leaf_type { type LeafKind = $kind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( + mapper: M, content: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { - M::map_leaf::<$content_type>(content) + mapper.map_leaf::<$content_type>(content) } fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + mapper: M, content: &'r Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { - M::map_leaf::<$content_type>(content) + mapper.map_leaf::<$content_type>(content) } fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + mapper: M, content: &'r mut Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>> { - M::map_leaf::<$content_type>(content) + mapper.map_leaf::<$content_type>(content) } fn content_to_leaf_kind( @@ -577,14 +593,14 @@ macro_rules! define_leaf_type { #[derive(Clone, Copy, PartialEq, Eq)] $kind_vis struct $kind; - impl From<$kind> for ValueLeafKind { + impl From<$kind> for AnyValueLeafKind { fn from(kind: $kind) -> Self { let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); - ValueLeafKind::from(as_parent_kind) + AnyValueLeafKind::from(as_parent_kind) } } - impl IsSpecificLeafKind for $kind { + impl IsLeafKind for $kind { fn source_type_name(&self) -> &'static str { $source_type_name } @@ -638,6 +654,12 @@ pub(crate) use define_leaf_type; pub(crate) struct DynMapper(std::marker::PhantomData); +impl DynMapper { + pub(crate) const fn new() -> Self { + Self(std::marker::PhantomData) + } +} + /// Implements `IsValueContent`, `IntoValueContent`, and `FromValueContent` for various /// form wrappers of a content type. This macro deduplicates code across `define_leaf_type`, /// `define_parent_type`, and `define_dyn_type`. @@ -692,33 +714,33 @@ macro_rules! impl_value_content_traits { } } - // BeMutable: content is MutableSubRcRefCell - impl<'a> IsValueContent<'a> for MutableSubRcRefCell { + // BeMutable: content is QqqMutable + impl<'a> IsValueContent<'a> for QqqMutable<$content_type> { type Type = $type_def; type Form = BeMutable; } - impl<'a> IntoValueContent<'a> for MutableSubRcRefCell { + impl<'a> IntoValueContent<'a> for QqqMutable<$content_type> { fn into_content(self) -> Self { self } } - impl<'a> FromValueContent<'a> for MutableSubRcRefCell { + impl<'a> FromValueContent<'a> for QqqMutable<$content_type> { fn from_content(content: Self) -> Self { content } } - // BeShared: content is SharedSubRcRefCell - impl<'a> IsValueContent<'a> for SharedSubRcRefCell { + // BeShared: content is QqqShared + impl<'a> IsValueContent<'a> for QqqShared<$content_type> { type Type = $type_def; type Form = BeShared; } - impl<'a> IntoValueContent<'a> for SharedSubRcRefCell { + impl<'a> IntoValueContent<'a> for QqqShared<$content_type> { fn into_content(self) -> Self { self } } - impl<'a> FromValueContent<'a> for SharedSubRcRefCell { + impl<'a> FromValueContent<'a> for QqqShared<$content_type> { fn from_content(content: Self) -> Self { content } @@ -863,33 +885,33 @@ macro_rules! impl_value_content_traits { } } - // BeMutable: content is MutableSubRcRefCell - impl<'a> IsValueContent<'a> for MutableSubRcRefCell { + // BeMutable: content is QqqMutable + impl<'a> IsValueContent<'a> for QqqMutable<$dyn_type> { type Type = $type_def; type Form = BeMutable; } - impl<'a> IntoValueContent<'a> for MutableSubRcRefCell { + impl<'a> IntoValueContent<'a> for QqqMutable<$dyn_type> { fn into_content(self) -> Self { self } } - impl<'a> FromValueContent<'a> for MutableSubRcRefCell { + impl<'a> FromValueContent<'a> for QqqMutable<$dyn_type> { fn from_content(content: Self) -> Self { content } } - // BeShared: content is SharedSubRcRefCell - impl<'a> IsValueContent<'a> for SharedSubRcRefCell { + // BeShared: content is QqqShared + impl<'a> IsValueContent<'a> for QqqShared<$dyn_type> { type Type = $type_def; type Form = BeShared; } - impl<'a> IntoValueContent<'a> for SharedSubRcRefCell { + impl<'a> IntoValueContent<'a> for QqqShared<$dyn_type> { fn into_content(self) -> Self { self } } - impl<'a> FromValueContent<'a> for SharedSubRcRefCell { + impl<'a> FromValueContent<'a> for QqqShared<$dyn_type> { fn from_content(content: Self) -> Self { content } @@ -1001,7 +1023,7 @@ macro_rules! define_dyn_type { for<'a> F: IsDynCompatibleForm = >::Content<'a>>, { fn downcast_from<'a>(content: >::Content<'a>) -> Option<>::Content<'a>> { - match T::map_with::<'a, F, DynMapper<$dyn_type>>(content) { + match T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content) { Ok(_) => panic!("DynMapper is expected to always short-circuit"), Err(dyn_leaf) => dyn_leaf, } @@ -1013,6 +1035,7 @@ macro_rules! define_dyn_type { type ShortCircuit<'a> = Option>; fn map_leaf<'a, L: IsValueLeaf>( + self, leaf: F::Leaf<'a, L>, ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> { diff --git a/src/expressions/control_flow.rs b/src/expressions/control_flow.rs index 853d2da7..6c77b8fc 100644 --- a/src/expressions/control_flow.rs +++ b/src/expressions/control_flow.rs @@ -203,7 +203,8 @@ impl Evaluate for WhileExpression { ExecutionOutcome::ControlFlow(control_flow_interrupt) => { match control_flow_interrupt { ControlFlowInterrupt::Break(break_interrupt) => { - return break_interrupt.into_value(self.span_range(), ownership); + return break_interrupt + .into_requested_value(self.span_range(), ownership); } ControlFlowInterrupt::Continue { .. } => { continue; @@ -285,7 +286,8 @@ impl Evaluate for LoopExpression { ExecutionOutcome::ControlFlow(control_flow_interrupt) => { match control_flow_interrupt { ControlFlowInterrupt::Break(break_interrupt) => { - return break_interrupt.into_value(self.span_range(), ownership); + return break_interrupt + .into_requested_value(self.span_range(), ownership); } ControlFlowInterrupt::Continue { .. } => { continue; @@ -391,7 +393,8 @@ impl Evaluate for ForExpression { ExecutionOutcome::ControlFlow(control_flow_interrupt) => { match control_flow_interrupt { ControlFlowInterrupt::Break(break_interrupt) => { - return break_interrupt.into_value(self.span_range(), ownership); + return break_interrupt + .into_requested_value(self.span_range(), ownership); } ControlFlowInterrupt::Continue { .. } => { continue; diff --git a/src/expressions/equality.rs b/src/expressions/equality.rs index 4fe2a2b0..4f3b78f2 100644 --- a/src/expressions/equality.rs +++ b/src/expressions/equality.rs @@ -302,8 +302,8 @@ pub(crate) enum DebugInequalityReason { }, /// Values have incompatible value kinds. ValueLeafKindMismatch { - lhs_kind: ValueLeafKind, - rhs_kind: ValueLeafKind, + lhs_kind: AnyValueLeafKind, + rhs_kind: AnyValueLeafKind, }, /// Ranges have incompatible structures. RangeStructureMismatch { diff --git a/src/expressions/evaluation/assignment_frames.rs b/src/expressions/evaluation/assignment_frames.rs index 3a8ba10c..d760685f 100644 --- a/src/expressions/evaluation/assignment_frames.rs +++ b/src/expressions/evaluation/assignment_frames.rs @@ -28,14 +28,14 @@ impl AnyAssignmentFrame { struct PrivateUnit; pub(super) struct AssigneeAssigner { - value: Value, + value: AnyValue, } impl AssigneeAssigner { pub(super) fn start( context: AssignmentContext, assignee: ExpressionNodeId, - value: Value, + value: AnyValue, ) -> NextAction { let frame = Self { value }; context.request_assignee(frame, assignee, true) @@ -67,7 +67,7 @@ impl GroupedAssigner { pub(super) fn start( context: AssignmentContext, inner: ExpressionNodeId, - value: Value, + value: AnyValue, ) -> NextAction { let frame = Self(PrivateUnit); context.request_assignment(frame, inner, value) @@ -93,7 +93,7 @@ impl EvaluationFrame for GroupedAssigner { pub(super) struct ArrayBasedAssigner { span_range: SpanRange, - assignee_stack: Vec<(ExpressionNodeId, Value)>, + assignee_stack: Vec<(ExpressionNodeId, AnyValue)>, } impl ArrayBasedAssigner { @@ -102,7 +102,7 @@ impl ArrayBasedAssigner { nodes: &Arena, brackets: &Brackets, assignee_item_node_ids: &[ExpressionNodeId], - value: Value, + value: AnyValue, ) -> ExecutionResult { let frame = Self::new(nodes, brackets.join(), assignee_item_node_ids, value)?; Ok(frame.handle_next_subassignment(context)) @@ -113,7 +113,7 @@ impl ArrayBasedAssigner { nodes: &Arena, assignee_span: Span, assignee_item_node_ids: &[ExpressionNodeId], - value: Value, + value: AnyValue, ) -> ExecutionResult { let span_range = assignee_span.span_range(); let array: ArrayValue = Spanned(value.into_owned(), span_range) @@ -231,7 +231,7 @@ impl ObjectBasedAssigner { context: AssignmentContext, braces: &Braces, assignee_pairs: &[(ObjectKey, ExpressionNodeId)], - value: Value, + value: AnyValue, ) -> ExecutionResult { let frame = Box::new(Self::new(braces.join(), assignee_pairs, value)?); frame.handle_next_subassignment(context) @@ -240,7 +240,7 @@ impl ObjectBasedAssigner { fn new( assignee_span: Span, assignee_pairs: &[(ObjectKey, ExpressionNodeId)], - value: Value, + value: AnyValue, ) -> ExecutionResult { let span_range = assignee_span.span_range(); let object: ObjectValue = Spanned(value.into_owned(), span_range) @@ -259,7 +259,7 @@ impl ObjectBasedAssigner { mut self: Box, context: AssignmentContext, access: IndexAccess, - index: &Value, + index: &AnyValue, assignee_node: ExpressionNodeId, ) -> ExecutionResult { let key: &str = index @@ -292,7 +292,7 @@ impl ObjectBasedAssigner { }) } - fn resolve_value(&mut self, key: String, key_span: Span) -> ExecutionResult { + fn resolve_value(&mut self, key: String, key_span: Span) -> ExecutionResult { if self.already_used_keys.contains(&key) { return key_span.syntax_err(format!("The key `{}` has already used", key)); } diff --git a/src/expressions/evaluation/evaluator.rs b/src/expressions/evaluation/evaluator.rs index f3a21729..79d33717 100644 --- a/src/expressions/evaluation/evaluator.rs +++ b/src/expressions/evaluation/evaluator.rs @@ -125,7 +125,7 @@ enum NextActionInner { // This covers atomic assignments and composite assignments // (similar to patterns but for existing values/reassignments) // let a = ["x", "y"]; let b; [a[1], .. b] = [1, 2, 3, 4] - ReadNodeAsAssignmentTarget(ExpressionNodeId, Value), + ReadNodeAsAssignmentTarget(ExpressionNodeId, AnyValue), HandleReturnedValue(Spanned), } @@ -372,7 +372,7 @@ impl<'a, T: RequestedValueType> Context<'a, T> { self, handler: H, node: ExpressionNodeId, - value: Value, + value: AnyValue, ) -> NextAction { self.stack .handlers diff --git a/src/expressions/evaluation/node_conversion.rs b/src/expressions/evaluation/node_conversion.rs index 1600014c..8ceb680d 100644 --- a/src/expressions/evaluation/node_conversion.rs +++ b/src/expressions/evaluation/node_conversion.rs @@ -134,7 +134,7 @@ impl ExpressionNode { // NB: This might intrisically be a part of a larger value, and might have been // created many lines previously, so doesn't have an obvious span associated with it // Instead, we put errors on the assignee syntax side - value: Value, + value: AnyValue, ) -> ExecutionResult { Ok(match self { ExpressionNode::Leaf(Leaf::Discarded(underscore)) => { diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 202a78ef..dbee547d 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -106,7 +106,7 @@ impl Spanned<&mut ArgumentValue> { } impl Deref for ArgumentValue { - type Target = Value; + type Target = AnyValue; fn deref(&self) -> &Self::Target { self.as_ref() @@ -119,8 +119,8 @@ impl AsMut for ArgumentValue { } } -impl AsRef for ArgumentValue { - fn as_ref(&self) -> &Value { +impl AsRef for ArgumentValue { + fn as_ref(&self) -> &AnyValue { match self { ArgumentValue::Owned(owned) => owned, ArgumentValue::Mutable(mutable) => mutable, @@ -409,7 +409,7 @@ impl ArgumentOwnership { ) -> ExecutionResult { match self { ArgumentOwnership::Owned => Ok(ArgumentValue::Owned( - copy_on_write.into_owned_transparently(span)?, + copy_on_write.clone_to_owned_transparently(span)?, )), ArgumentOwnership::Shared => Ok(ArgumentValue::Shared(copy_on_write.into_shared())), ArgumentOwnership::Mutable => { @@ -417,7 +417,7 @@ impl ArgumentOwnership { span.ownership_err("A mutable reference is required, but a shared reference was received, this indicates a possible bug as the updated value won't be accessible. To proceed regardless, use `.clone()` to get a mutable reference to a cloned value.") } else { Ok(ArgumentValue::Mutable(Mutable::new_from_owned( - copy_on_write.into_owned_transparently(span)?, + copy_on_write.clone_to_owned_transparently(span)?, ))) } } @@ -609,7 +609,7 @@ impl EvaluationFrame for GroupBuilder { pub(super) struct ArrayBuilder { span: Span, unevaluated_items: Vec, - evaluated_items: Vec, + evaluated_items: Vec, } impl ArrayBuilder { @@ -960,8 +960,11 @@ impl EvaluationFrame for ValuePropertyAccessBuilder { ) -> ExecutionResult { let auto_create = context.requested_ownership().requests_auto_create(); let mapped = value.expect_any_value_and_map( - |shared| shared.try_map(|value| value.property_ref(&self.access)), - |mutable| mutable.try_map(|value| value.property_mut(&self.access, auto_create)), + |shared| shared.try_map(|value| value.as_ref_value().property_ref(&self.access)), + |mutable| { + mutable + .try_map(|value| value.as_mut_value().property_mut(&self.access, auto_create)) + }, |owned| owned.try_map(|value| value.into_property(&self.access)), )?; // The result span covers source through property @@ -1027,13 +1030,19 @@ impl EvaluationFrame for ValueIndexAccessBuilder { source: Spanned(source, source_span), } => { let index = value.expect_shared(); - let index = index.as_ref().spanned(span); + let index = index.as_ref_value().spanned(span); let auto_create = context.requested_ownership().requests_auto_create(); let result = source.expect_any_value_and_map( - |shared| shared.try_map(|value| value.index_ref(self.access, index)), + |shared| { + shared.try_map(|value| value.as_ref_value().index_ref(self.access, index)) + }, |mutable| { - mutable.try_map(|value| value.index_mut(self.access, index, auto_create)) + mutable.try_map(|value| { + value + .as_mut_value() + .index_mut(self.access, index, auto_create) + }) }, |owned| owned.try_map(|value| value.into_indexed(self.access, index)), )?; @@ -1051,7 +1060,7 @@ pub(super) struct RangeBuilder { enum RangePath { OnLeftBranch { right: Option }, - OnRightBranch { left: Option }, + OnRightBranch { left: Option }, } impl RangeBuilder { diff --git a/src/expressions/expression_block.rs b/src/expressions/expression_block.rs index f7686874..306b7e04 100644 --- a/src/expressions/expression_block.rs +++ b/src/expressions/expression_block.rs @@ -32,7 +32,7 @@ impl HasSpanRange for EmbeddedExpression { impl Interpret for EmbeddedExpression { fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { let value = self.content.evaluate_shared(interpreter)?; - value.output_to( + value.as_ref_value().output_to( Grouping::Flattened, &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), ) @@ -75,7 +75,7 @@ impl Interpret for EmbeddedStatements { .evaluate_spanned(interpreter, self.span_range(), RequestedOwnership::shared())? .0 .expect_shared(); - value.output_to( + value.as_ref_value().output_to( Grouping::Flattened, &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), ) @@ -160,7 +160,7 @@ impl Evaluate for ExpressionBlock { match interpreter.catch_control_flow(output_result, *catch_location, scope)? { ExecutionOutcome::Value(Spanned(value, _)) => value, ExecutionOutcome::ControlFlow(ControlFlowInterrupt::Break(break_interrupt)) => { - break_interrupt.into_value(self.span_range(), ownership)? + break_interrupt.into_requested_value(self.span_range(), ownership)? } ExecutionOutcome::ControlFlow(_) => { unreachable!("Only break control flow should be catchable by labeled blocks") diff --git a/src/expressions/expression_parsing.rs b/src/expressions/expression_parsing.rs index 3e27932d..77f2078b 100644 --- a/src/expressions/expression_parsing.rs +++ b/src/expressions/expression_parsing.rs @@ -153,7 +153,7 @@ impl<'a> ExpressionParser<'a> { let lit: syn::Lit = input.parse()?; let span_range = lit.span().span_range(); UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(Value::for_syn_lit(lit)), + SharedValue::new_from_owned(AnyValue::for_syn_lit(lit)), span_range, ))) }, diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index 2d248a03..4b143219 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -106,7 +106,7 @@ impl UnaryOperation { } #[derive(Copy, Clone)] -pub(crate) struct CastTarget(pub(crate) ValueLeafKind); +pub(crate) struct CastTarget(pub(crate) AnyValueLeafKind); impl CastTarget { fn from_source_type(s: TypeIdent) -> ParseResult { @@ -129,10 +129,10 @@ impl CastTarget { pub(crate) fn is_singleton_target(&self) -> bool { matches!( self.0, - ValueLeafKind::Bool(_) - | ValueLeafKind::Char(_) - | ValueLeafKind::Integer(_) - | ValueLeafKind::Float(_) + AnyValueLeafKind::Bool(_) + | AnyValueLeafKind::Char(_) + | AnyValueLeafKind::Integer(_) + | AnyValueLeafKind::Float(_) ) } } @@ -277,7 +277,7 @@ impl SynParse for BinaryOperation { impl BinaryOperation { pub(super) fn lazy_evaluate( &self, - left: Spanned<&Value>, + left: Spanned<&AnyValue>, ) -> ExecutionResult> { match self { BinaryOperation::LogicalAnd { .. } => { diff --git a/src/expressions/patterns.rs b/src/expressions/patterns.rs index 338b80a1..27d02ed5 100644 --- a/src/expressions/patterns.rs +++ b/src/expressions/patterns.rs @@ -4,7 +4,7 @@ pub(crate) trait HandleDestructure { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()>; } @@ -68,7 +68,7 @@ impl HandleDestructure for Pattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { match self { Pattern::Variable(variable) => variable.handle_destructure(interpreter, value), @@ -111,7 +111,7 @@ impl HandleDestructure for ArrayPattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { let array: ArrayValue = Spanned(value.into_owned(), self.brackets.span_range()) .resolve_as("The value destructured with an array pattern")?; @@ -226,7 +226,7 @@ impl HandleDestructure for ObjectPattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { let object: ObjectValue = Spanned(value.into_owned(), self.braces.span_range()) .resolve_as("The value destructured with an object pattern")?; @@ -352,7 +352,7 @@ impl HandleDestructure for StreamPattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { let stream = Spanned(value, self.brackets.span_range()) .downcast_resolve("The value destructured with a stream pattern")?; @@ -394,7 +394,7 @@ impl HandleDestructure for ParseTemplatePattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { let stream = Spanned(value, self.brackets.span_range()) .downcast_resolve("The value destructured with a parse template pattern")?; diff --git a/src/expressions/statements.rs b/src/expressions/statements.rs index ee45464f..adc76fd5 100644 --- a/src/expressions/statements.rs +++ b/src/expressions/statements.rs @@ -369,7 +369,7 @@ impl EmitStatement { interpreter: &mut Interpreter, ) -> ExecutionResult<()> { let Spanned(value, span) = self.expression.evaluate_owned(interpreter)?; - value.output_to( + value.as_ref_value().output_to( Grouping::Flattened, &mut ToStreamContext::new(interpreter.output(&self.emit)?, span), ) diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index 51a21965..97c857a4 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -50,7 +50,7 @@ pub(crate) trait IsArgument: Sized { } impl IsArgument for ArgumentValue { - type ValueType = ValueType; + type ValueType = AnyType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; fn from_argument(Spanned(value, _): Spanned) -> ExecutionResult { @@ -58,7 +58,7 @@ impl IsArgument for ArgumentValue { } } -impl + ResolvableArgumentTarget + ?Sized> IsArgument for Shared { +impl + ResolvableArgumentTarget + ?Sized> IsArgument for Shared { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; @@ -79,7 +79,9 @@ where } } -impl + ResolvableArgumentTarget + ?Sized> IsArgument for Assignee { +impl + ResolvableArgumentTarget + ?Sized> IsArgument + for Assignee +{ type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Assignee { auto_create: false }; @@ -88,7 +90,7 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgument } } -impl + ResolvableArgumentTarget + ?Sized> IsArgument for Mutable { +impl + ResolvableArgumentTarget + ?Sized> IsArgument for Mutable { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; @@ -109,7 +111,7 @@ where } } -impl + ResolvableArgumentTarget> IsArgument for Owned { +impl + ResolvableArgumentTarget> IsArgument for Owned { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; @@ -118,18 +120,22 @@ impl + ResolvableArgumentTarget> IsArgument for Owned< } } -impl + ResolvableArgumentTarget> IsArgument for T { +impl + ResolvableArgumentTarget> IsArgument for T { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; fn from_argument(argument: Spanned) -> ExecutionResult { - T::resolve_value(argument.expect_owned(), "This argument") + T::resolve_value( + argument.expect_owned().map(|owned| owned.0), + "This argument", + ) } } -impl + ResolvableArgumentTarget + ToOwned> IsArgument for CopyOnWrite +impl + ResolvableArgumentTarget + ToOwned> IsArgument + for CopyOnWrite where - T::Owned: ResolvableOwned, + T::Owned: ResolvableOwned, { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; @@ -138,7 +144,7 @@ where value.expect_copy_on_write().map( |v| T::resolve_shared(v.spanned(span), "This argument"), |v| { - >::resolve_owned( + >::resolve_owned( v.spanned(span), "This argument", ) @@ -164,51 +170,55 @@ pub(crate) trait ResolveAs { impl, V> ResolveAs for Spanned> { fn resolve_as(self, resolution_target: &str) -> ExecutionResult { - T::resolve_value(self, resolution_target) + T::resolve_value(self.map(|owned| owned.0), resolution_target) } } // Sadly this can't be changed Value => V because of spurious issues with // https://github.com/rust-lang/rust/issues/48869 // Instead, we could introduce a different trait ResolveAs2 if needed. -impl> ResolveAs> for Spanned> { +impl> ResolveAs> for Spanned> { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_owned(self, resolution_target) } } -impl + ?Sized> ResolveAs> for Spanned> { +impl + ?Sized> ResolveAs> for Spanned> { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_shared(self, resolution_target) } } -impl<'a, T: ResolvableShared + ?Sized> ResolveAs<&'a T> for Spanned<&'a Value> { +impl<'a, T: ResolvableShared + ?Sized> ResolveAs<&'a T> for Spanned<&'a AnyValue> { fn resolve_as(self, resolution_target: &str) -> ExecutionResult<&'a T> { T::resolve_ref(self, resolution_target) } } -impl<'a, T: ResolvableShared + ?Sized> ResolveAs> for Spanned<&'a Value> { +impl<'a, T: ResolvableShared + ?Sized> ResolveAs> + for Spanned<&'a AnyValue> +{ fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_spanned_ref(self, resolution_target) } } -impl + ?Sized> ResolveAs> for Spanned> { +impl + ?Sized> ResolveAs> for Spanned> { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_mutable(self, resolution_target) } } -impl<'a, T: ResolvableMutable + ?Sized> ResolveAs<&'a mut T> for Spanned<&'a mut Value> { +impl<'a, T: ResolvableMutable + ?Sized> ResolveAs<&'a mut T> + for Spanned<&'a mut AnyValue> +{ fn resolve_as(self, resolution_target: &str) -> ExecutionResult<&'a mut T> { T::resolve_ref_mut(self, resolution_target) } } -impl<'a, T: ResolvableMutable + ?Sized> ResolveAs> - for Spanned<&'a mut Value> +impl<'a, T: ResolvableMutable + ?Sized> ResolveAs> + for Spanned<&'a mut AnyValue> { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_spanned_ref_mut(self, resolution_target) @@ -239,14 +249,14 @@ pub(crate) trait ResolvableOwned: Sized { /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" fn resolve_value( - Spanned(value, span): Spanned>, + Spanned(value, span): Spanned, resolution_target: &str, ) -> ExecutionResult { let context = ResolutionContext { span_range: &span, resolution_target, }; - Self::resolve_from_value(value.into_inner(), context) + Self::resolve_from_value(value, context) } /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" @@ -370,26 +380,26 @@ pub(crate) trait ResolvableMutable { } } -impl ResolvableArgumentTarget for Value { - type ValueType = ValueType; +impl ResolvableArgumentTarget for AnyValue { + type ValueType = AnyType; } -impl ResolvableOwned for Value { - fn resolve_from_value(value: Value, _context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for AnyValue { + fn resolve_from_value(value: AnyValue, _context: ResolutionContext) -> ExecutionResult { Ok(value) } } -impl ResolvableShared for Value { +impl ResolvableShared for AnyValue { fn resolve_from_ref<'a>( - value: &'a Value, + value: &'a AnyValue, _context: ResolutionContext, ) -> ExecutionResult<&'a Self> { Ok(value) } } -impl ResolvableMutable for Value { +impl ResolvableMutable for AnyValue { fn resolve_from_mut<'a>( - value: &'a mut Value, + value: &'a mut AnyValue, _context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { Ok(value) @@ -402,27 +412,27 @@ macro_rules! impl_resolvable_argument_for { type ValueType = $value_type; } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - $value: Value, + $value: AnyValue, $context: ResolutionContext, ) -> ExecutionResult { $body } } - impl ResolvableShared for $type { + impl ResolvableShared for $type { fn resolve_from_ref<'a>( - $value: &'a Value, + $value: &'a AnyValue, $context: ResolutionContext, ) -> ExecutionResult<&'a Self> { $body } } - impl ResolvableMutable for $type { + impl ResolvableMutable for $type { fn resolve_from_mut<'a>( - $value: &'a mut Value, + $value: &'a mut AnyValue, $context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { $body diff --git a/src/expressions/type_resolution/outputs.rs b/src/expressions/type_resolution/outputs.rs index aa97a515..f79c7677 100644 --- a/src/expressions/type_resolution/outputs.rs +++ b/src/expressions/type_resolution/outputs.rs @@ -26,13 +26,13 @@ impl IsReturnable for ReturnedValue { } } -impl IsReturnable for Shared { +impl IsReturnable for Shared { fn to_returned_value(self) -> ExecutionResult { Ok(ReturnedValue::Shared(self)) } } -impl IsReturnable for Mutable { +impl IsReturnable for Mutable { fn to_returned_value(self) -> ExecutionResult { Ok(ReturnedValue::Mutable(self)) } diff --git a/src/expressions/type_resolution/type_data.rs b/src/expressions/type_resolution/type_data.rs index ae7d80ff..646f858d 100644 --- a/src/expressions/type_resolution/type_data.rs +++ b/src/expressions/type_resolution/type_data.rs @@ -18,7 +18,7 @@ pub(crate) trait TypeFeatureResolver { ) -> Option; /// Resolves a property of this type. - fn resolve_type_property(&self, _property_name: &str) -> Option; + fn resolve_type_property(&self, _property_name: &str) -> Option; } pub(crate) trait TypeData { @@ -45,7 +45,7 @@ pub(crate) trait TypeData { /// Returns a property on the type. /// Properties are *not* currently resolved up the resolution chain... but maybe they should be? /// ... similarly, maybe functions should be too, when they are added? - fn resolve_type_property(_property_name: &str) -> Option { + fn resolve_type_property(_property_name: &str) -> Option { None } } diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs index ef7ca61a..15a545bc 100644 --- a/src/expressions/type_resolution/type_kinds.rs +++ b/src/expressions/type_resolution/type_kinds.rs @@ -2,13 +2,13 @@ use super::*; /// A trait for specific value kinds that can provide a display name. /// This is implemented by `ValueLeafKind`, `IntegerKind`, `FloatKind`, etc. -pub(crate) trait IsSpecificLeafKind: Copy + Into { +pub(crate) trait IsLeafKind: Copy + Into { fn source_type_name(&self) -> &'static str; fn articled_display_name(&self) -> &'static str; fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver; } -impl ValueLeafKind { +impl AnyValueLeafKind { /// This should be true for types which users expect to have value /// semantics, but false for types which are expensive to clone or /// are expected to have reference semantics. @@ -17,38 +17,60 @@ impl ValueLeafKind { /// when doing method resolution. pub(crate) fn supports_transparent_cloning(&self) -> bool { match self { - ValueLeafKind::None(_) => true, - ValueLeafKind::Integer(_) => true, - ValueLeafKind::Float(_) => true, - ValueLeafKind::Bool(_) => true, + AnyValueLeafKind::None(_) => true, + AnyValueLeafKind::Integer(_) => true, + AnyValueLeafKind::Float(_) => true, + AnyValueLeafKind::Bool(_) => true, // Strings are value-like, so it makes sense to transparently clone them - ValueLeafKind::String(_) => true, - ValueLeafKind::Char(_) => true, - ValueLeafKind::UnsupportedLiteral(_) => false, - ValueLeafKind::Array(_) => false, - ValueLeafKind::Object(_) => false, - ValueLeafKind::Stream(_) => false, - ValueLeafKind::Range(_) => true, - ValueLeafKind::Iterator(_) => false, + AnyValueLeafKind::String(_) => true, + AnyValueLeafKind::Char(_) => true, + AnyValueLeafKind::UnsupportedLiteral(_) => false, + AnyValueLeafKind::Array(_) => false, + AnyValueLeafKind::Object(_) => false, + AnyValueLeafKind::Stream(_) => false, + AnyValueLeafKind::Range(_) => true, + AnyValueLeafKind::Iterator(_) => false, // A parser is a handle, so can be cloned transparently. // It may fail to be able to be used to parse if the underlying stream is out of scope of course. - ValueLeafKind::Parser(_) => true, + AnyValueLeafKind::Parser(_) => true, } } } -// A ValueLeafKind represents a kind of leaf value. +// A AnyValueLeafKind represents a kind of leaf value. // But a TypeKind represents a type in the hierarchy, which points at a type data. pub(crate) enum TypeKind { - Leaf(ValueLeafKind), + Leaf(AnyValueLeafKind), Parent(ParentTypeKind), Dyn(DynTypeKind), } impl TypeKind { + /// This should be true for types which users expect to have value + /// semantics, but false for types which are expensive to clone or + /// are expected to have reference semantics. + /// + /// This indicates if an &x can be converted to an x via cloning + /// when doing method resolution. + pub(crate) fn supports_transparent_cloning(&self) -> bool { + match self { + TypeKind::Leaf(leaf_kind) => leaf_kind.supports_transparent_cloning(), + TypeKind::Parent(_) => false, + TypeKind::Dyn(_) => false, + } + } + + pub(crate) fn articled_display_name(&self) -> &'static str { + match self { + TypeKind::Leaf(leaf_kind) => leaf_kind.articled_display_name(), + TypeKind::Parent(parent_kind) => parent_kind.articled_display_name(), + TypeKind::Dyn(dyn_kind) => dyn_kind.articled_display_name(), + } + } + pub(crate) fn from_source_name(name: &str) -> Option { // Parse Leaf and Parent TypeKinds - if let Some(tk) = ::type_kind_from_source_name(name) { + if let Some(tk) = ::type_kind_from_source_name(name) { return Some(tk); } // Parse Dyn TypeKinds @@ -68,12 +90,20 @@ impl TypeKind { } pub(crate) enum ParentTypeKind { - Value(ValueTypeKind), + Value(AnyValueTypeKind), Integer(IntegerTypeKind), Float(FloatTypeKind), } impl ParentTypeKind { + pub(crate) fn articled_display_name(&self) -> &'static str { + match self { + ParentTypeKind::Value(x) => x.articled_display_name(), + ParentTypeKind::Integer(x) => x.articled_display_name(), + ParentTypeKind::Float(x) => x.articled_display_name(), + } + } + pub(crate) fn source_name(&self) -> &'static str { match self { ParentTypeKind::Value(x) => x.source_type_name(), @@ -96,6 +126,12 @@ pub(crate) enum DynTypeKind { } impl DynTypeKind { + pub(crate) fn articled_display_name(&self) -> &'static str { + match self { + DynTypeKind::Iterable => IterableType::ARTICLED_DISPLAY_NAME, + } + } + pub(crate) fn from_source_name(name: &str) -> Option { match name { IterableType::SOURCE_TYPE_NAME => Some(DynTypeKind::Iterable), diff --git a/src/expressions/values/value.rs b/src/expressions/values/any_value.rs similarity index 65% rename from src/expressions/values/value.rs rename to src/expressions/values/any_value.rs index 052fafdf..afdab3d8 100644 --- a/src/expressions/values/value.rs +++ b/src/expressions/values/any_value.rs @@ -1,15 +1,14 @@ use super::*; -// TODO[concepts]: Uncomment when ready -// pub(crate) type OwnedValue = QqqOwned; -// pub(crate) type ValueRef<'a> = QqqRef<'a, ValueType>; -// pub(crate) type ValueMut<'a> = QqqMut<'a, ValueType>; +pub(crate) type AnyValue = AnyValueContent<'static, BeOwned>; +pub(crate) type AnyValueRef<'a> = AnyValueContent<'a, BeRef>; +pub(crate) type AnyValueMut<'a> = AnyValueContent<'a, BeMut>; define_parent_type! { - pub(crate) ValueType, - content: pub(crate) ValueContent, - leaf_kind: pub(crate) ValueLeafKind, - type_kind: ParentTypeKind::Value(pub(crate) ValueTypeKind), + pub(crate) AnyType, + content: pub(crate) AnyValueContent, + leaf_kind: pub(crate) AnyValueLeafKind, + type_kind: ParentTypeKind::Value(pub(crate) AnyValueTypeKind), variants: { None => NoneType, Integer => IntegerType, @@ -31,14 +30,12 @@ define_parent_type! { articled_display_name: "any value", } -pub(crate) type Value = ValueContent<'static, BeOwned>; - define_type_features! { - impl ValueType, + impl AnyType, pub(crate) mod value_interface { pub(crate) mod methods { fn clone(this: CopyOnWriteValue) -> OwnedValue { - this.into_owned_infallible() + this.clone_to_owned_infallible() } fn as_mut(Spanned(this, span): Spanned) -> ExecutionResult { @@ -65,35 +62,35 @@ define_type_features! { core::mem::swap(a.0.deref_mut(), b.0.deref_mut()); } - fn replace(mut a: AssigneeValue, b: Value) -> Value { + fn replace(mut a: AssigneeValue, b: AnyValue) -> AnyValue { core::mem::replace(a.0.deref_mut(), b) } fn debug(Spanned(this, span_range): Spanned) -> ExecutionResult<()> { - let message = this.concat_recursive(&ConcatBehaviour::debug(span_range))?; + let message = this.as_ref_value().concat_recursive(&ConcatBehaviour::debug(span_range))?; span_range.debug_err(message) } fn to_debug_string(Spanned(this, span_range): Spanned) -> ExecutionResult { - this.concat_recursive(&ConcatBehaviour::debug(span_range)) + this.as_ref_value().concat_recursive(&ConcatBehaviour::debug(span_range)) } fn to_stream(Spanned(input, span_range): Spanned) -> ExecutionResult { input.map_into( - |shared| shared.output_to_new_stream(Grouping::Flattened, span_range), + |shared| shared.as_ref_value().output_to_new_stream(Grouping::Flattened, span_range), |owned| owned.0.into_stream(Grouping::Flattened, span_range), ) } fn to_group(Spanned(input, span_range): Spanned) -> ExecutionResult { input.map_into( - |shared| shared.output_to_new_stream(Grouping::Grouped, span_range), + |shared| shared.as_ref_value().output_to_new_stream(Grouping::Grouped, span_range), |owned| owned.0.into_stream(Grouping::Grouped, span_range), ) } fn to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { - input.concat_recursive(&ConcatBehaviour::standard(span_range)) + input.as_ref_value().concat_recursive(&ConcatBehaviour::standard(span_range)) } [context] fn with_span(value: Spanned, spans: AnyRef) -> ExecutionResult { @@ -115,70 +112,68 @@ define_type_features! { // EQUALITY METHODS // =============================== // Compare values with strict type checking - errors on value kind mismatch. - [context] fn typed_eq(this: AnyRef, other: AnyRef) -> ExecutionResult { - let this_value: &Value = &this; - let other_value: &Value = &other; - this_value.typed_eq(other_value, context.span_range()) + [context] fn typed_eq(this: AnyRef, other: AnyRef) -> ExecutionResult { + this.as_ref_value().typed_eq(&other.as_ref_value(), context.span_range()) } // STRING-BASED CONVERSION METHODS // =============================== - [context] fn to_ident(this: Spanned) -> ExecutionResult { + [context] fn to_ident(this: Spanned) -> ExecutionResult { let stream = this.into_stream()?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_ident(context, spanned) } - [context] fn to_ident_camel(this: Spanned) -> ExecutionResult { + [context] fn to_ident_camel(this: Spanned) -> ExecutionResult { let stream = this.into_stream()?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_ident_camel(context, spanned) } - [context] fn to_ident_snake(this: Spanned) -> ExecutionResult { + [context] fn to_ident_snake(this: Spanned) -> ExecutionResult { let stream = this.into_stream()?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_ident_snake(context, spanned) } - [context] fn to_ident_upper_snake(this: Spanned) -> ExecutionResult { + [context] fn to_ident_upper_snake(this: Spanned) -> ExecutionResult { let stream = this.into_stream()?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_ident_upper_snake(context, spanned) } // Some literals become Value::UnsupportedLiteral but can still be round-tripped back to a stream - [context] fn to_literal(this: Spanned) -> ExecutionResult { + [context] fn to_literal(this: Spanned) -> ExecutionResult { let stream = this.into_stream()?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_literal(context, spanned) } } pub(crate) mod unary_operations { - fn cast_to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { - input.concat_recursive(&ConcatBehaviour::standard(span_range)) + fn cast_to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { + input.as_ref_value().concat_recursive(&ConcatBehaviour::standard(span_range)) } - fn cast_to_stream(input: Spanned) -> ExecutionResult { + fn cast_to_stream(input: Spanned) -> ExecutionResult { input.into_stream() } } pub(crate) mod binary_operations { - fn eq(lhs: AnyRef, rhs: AnyRef) -> bool { - Value::values_equal(&lhs, &rhs) + fn eq(lhs: AnyRef, rhs: AnyRef) -> bool { + AnyValue::values_equal(lhs.as_ref_value(), rhs.as_ref_value()) } - fn ne(lhs: AnyRef, rhs: AnyRef) -> bool { - !Value::values_equal(&lhs, &rhs) + fn ne(lhs: AnyRef, rhs: AnyRef) -> bool { + !AnyValue::values_equal(lhs.as_ref_value(), rhs.as_ref_value()) } } interface_items { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { - ValueLeafKind::String(_) => unary_definitions::cast_to_string(), - ValueLeafKind::Stream(_) => unary_definitions::cast_to_stream(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::Stream(_) => unary_definitions::cast_to_stream(), _ => return None, }, _ => return None, @@ -199,7 +194,7 @@ define_type_features! { } pub(crate) trait IntoValue: Sized { - fn into_value(self) -> Value; + fn into_value(self) -> AnyValue; fn into_owned(self) -> Owned { Owned::new(self) } @@ -208,7 +203,13 @@ pub(crate) trait IntoValue: Sized { } } -impl Value { +impl<'a, F: IsHierarchicalForm> AnyValueContent<'a, F> { + pub(crate) fn is_none(&self) -> bool { + matches!(&self, AnyValueContent::None(_)) + } +} + +impl AnyValue { pub(crate) fn for_literal(literal: Literal) -> OwnedValue { // The unwrap should be safe because all Literal should be parsable // as syn::Lit; falling back to syn::Lit::Verbatim if necessary. @@ -242,10 +243,11 @@ impl Value { } } + // TODO[concepts]: Remove from here pub(crate) fn try_transparent_clone( &self, error_span_range: SpanRange, - ) -> ExecutionResult { + ) -> ExecutionResult { if !self.value_kind().supports_transparent_cloning() { return error_span_range.ownership_err(format!( "An owned value is required, but a reference was received, and {} does not support transparent cloning. You may wish to use .clone() explicitly.", @@ -255,118 +257,70 @@ impl Value { Ok(self.clone()) } - pub(crate) fn is_none(&self) -> bool { - matches!(&self, ValueContent::None(_)) + pub(crate) fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { + self.as_ref_value() + .test_equality(&other.as_ref_value(), ctx) } /// Recursively compares two values for equality using `ValuesEqual` semantics. - pub(crate) fn values_equal(lhs: &Value, rhs: &Value) -> bool { - lhs.lenient_eq(rhs) + pub(crate) fn values_equal<'a>(lhs: AnyValueRef<'a>, rhs: AnyValueRef<'a>) -> bool { + lhs.lenient_eq(&rhs) } } -impl ValuesEqual for Value { +impl<'a> ValuesEqual for AnyValueRef<'a> { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { // Each variant has two lines: same-type comparison, then type-mismatch fallback. // This ensures adding a new variant only requires adding two lines at the bottom. match (self, other) { - (ValueContent::None(_), ValueContent::None(_)) => ctx.values_equal(), - (ValueContent::None(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Bool(l), ValueContent::Bool(r)) => l.test_equality(r, ctx), - (ValueContent::Bool(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Char(l), ValueContent::Char(r)) => l.test_equality(r, ctx), - (ValueContent::Char(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::String(l), ValueContent::String(r)) => l.test_equality(r, ctx), - (ValueContent::String(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Integer(l), ValueContent::Integer(r)) => l.test_equality(r, ctx), - (ValueContent::Integer(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Float(l), ValueContent::Float(r)) => l.test_equality(r, ctx), - (ValueContent::Float(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Array(l), ValueContent::Array(r)) => l.test_equality(r, ctx), - (ValueContent::Array(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Object(l), ValueContent::Object(r)) => l.test_equality(r, ctx), - (ValueContent::Object(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Stream(l), ValueContent::Stream(r)) => l.test_equality(r, ctx), - (ValueContent::Stream(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Range(l), ValueContent::Range(r)) => l.test_equality(r, ctx), - (ValueContent::Range(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::UnsupportedLiteral(l), ValueContent::UnsupportedLiteral(r)) => { + (AnyValueContent::None(_), AnyValueContent::None(_)) => ctx.values_equal(), + (AnyValueContent::None(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Bool(l), AnyValueContent::Bool(r)) => l.test_equality(r, ctx), + (AnyValueContent::Bool(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Char(l), AnyValueContent::Char(r)) => l.test_equality(r, ctx), + (AnyValueContent::Char(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::String(l), AnyValueContent::String(r)) => l.test_equality(r, ctx), + (AnyValueContent::String(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Integer(l), AnyValueContent::Integer(r)) => l.test_equality(r, ctx), + (AnyValueContent::Integer(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Float(l), AnyValueContent::Float(r)) => l.test_equality(r, ctx), + (AnyValueContent::Float(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Array(l), AnyValueContent::Array(r)) => l.test_equality(r, ctx), + (AnyValueContent::Array(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Object(l), AnyValueContent::Object(r)) => l.test_equality(r, ctx), + (AnyValueContent::Object(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Stream(l), AnyValueContent::Stream(r)) => l.test_equality(r, ctx), + (AnyValueContent::Stream(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Range(l), AnyValueContent::Range(r)) => l.test_equality(r, ctx), + (AnyValueContent::Range(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::UnsupportedLiteral(l), AnyValueContent::UnsupportedLiteral(r)) => { l.test_equality(r, ctx) } - (ValueContent::UnsupportedLiteral(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Parser(l), ValueContent::Parser(r)) => l.test_equality(r, ctx), - (ValueContent::Parser(_), _) => ctx.kind_mismatch(self, other), - (ValueContent::Iterator(l), ValueContent::Iterator(r)) => l.test_equality(r, ctx), - (ValueContent::Iterator(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::UnsupportedLiteral(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Parser(l), AnyValueContent::Parser(r)) => l.test_equality(r, ctx), + (AnyValueContent::Parser(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Iterator(l), AnyValueContent::Iterator(r)) => l.test_equality(r, ctx), + (AnyValueContent::Iterator(_), _) => ctx.kind_mismatch(self, other), } } } -impl Value { +impl AnyValue { pub(crate) fn into_indexed( self, access: IndexAccess, - index: Spanned<&Self>, + index: Spanned, ) -> ExecutionResult { match self { - ValueContent::Array(array) => array.into_indexed(index), - ValueContent::Object(object) => object.into_indexed(index), - other => access.type_err(format!("Cannot index into {}", other.articled_kind())), - } - } - - pub(crate) fn index_mut( - &mut self, - access: IndexAccess, - index: Spanned<&Self>, - auto_create: bool, - ) -> ExecutionResult<&mut Self> { - match self { - ValueContent::Array(array) => array.index_mut(index), - ValueContent::Object(object) => object.index_mut(index, auto_create), - other => access.type_err(format!("Cannot index into {}", other.articled_kind())), - } - } - - pub(crate) fn index_ref( - &self, - access: IndexAccess, - index: Spanned<&Self>, - ) -> ExecutionResult<&Self> { - match self { - ValueContent::Array(array) => array.index_ref(index), - ValueContent::Object(object) => object.index_ref(index), + AnyValueContent::Array(array) => array.into_indexed(index), + AnyValueContent::Object(object) => object.into_indexed(index), other => access.type_err(format!("Cannot index into {}", other.articled_kind())), } } pub(crate) fn into_property(self, access: &PropertyAccess) -> ExecutionResult { match self { - ValueContent::Object(object) => object.into_property(access), - other => access.type_err(format!( - "Cannot access properties on {}", - other.articled_kind() - )), - } - } - - pub(crate) fn property_mut( - &mut self, - access: &PropertyAccess, - auto_create: bool, - ) -> ExecutionResult<&mut Self> { - match self { - ValueContent::Object(object) => object.property_mut(access, auto_create), - other => access.type_err(format!( - "Cannot access properties on {}", - other.articled_kind() - )), - } - } - - pub(crate) fn property_ref(&self, access: &PropertyAccess) -> ExecutionResult<&Self> { - match self { - ValueContent::Object(object) => object.property_ref(access), + AnyValueContent::Object(object) => object.into_property(access), other => access.type_err(format!( "Cannot access properties on {}", other.articled_kind() @@ -380,19 +334,23 @@ impl Value { error_span_range: SpanRange, ) -> ExecutionResult { match (self, grouping) { - (ValueContent::Stream(value), Grouping::Flattened) => Ok(value), - (ValueContent::Stream(value), Grouping::Grouped) => { + (AnyValueContent::Stream(value), Grouping::Flattened) => Ok(value), + (AnyValueContent::Stream(value), Grouping::Grouped) => { let mut output: OutputStream = OutputStream::new(); let span = ToStreamContext::new(&mut output, error_span_range).new_token_span(); output.push_new_group(value, Delimiter::None, span); Ok(output) } - (other, grouping) => other.output_to_new_stream(grouping, error_span_range), + (other, grouping) => other + .as_ref_value() + .output_to_new_stream(grouping, error_span_range), } } +} +impl<'a> AnyValueRef<'a> { pub(crate) fn output_to_new_stream( - &self, + self, grouping: Grouping, error_span_range: SpanRange, ) -> ExecutionResult { @@ -405,7 +363,7 @@ impl Value { } pub(crate) fn output_to( - &self, + self, grouping: Grouping, output: &mut ToStreamContext, ) -> ExecutionResult<()> { @@ -424,93 +382,95 @@ impl Value { Ok(()) } - fn output_flattened_to(&self, output: &mut ToStreamContext) -> ExecutionResult<()> { + fn output_flattened_to(self, output: &mut ToStreamContext) -> ExecutionResult<()> { match self { - ValueContent::None(_) => {} - ValueContent::Integer(value) => { - let literal = value.to_literal(output.new_token_span()); + AnyValueContent::None(_) => {} + AnyValueContent::Integer(value) => { + let literal = value + .clone_to_owned_infallible() + .to_literal(output.new_token_span()); output.push_literal(literal); } - ValueContent::Float(value) => { - value.output_to(output); + AnyValueContent::Float(value) => { + value.clone_to_owned_infallible().output_to(output); } - ValueContent::Bool(value) => { + AnyValueContent::Bool(value) => { let ident = Ident::new_bool(*value, output.new_token_span()); output.push_ident(ident); } - ValueContent::String(value) => { + AnyValueContent::String(value) => { let literal = Literal::string(value).with_span(output.new_token_span()); output.push_literal(literal); } - ValueContent::Char(value) => { + AnyValueContent::Char(value) => { let literal = Literal::character(*value).with_span(output.new_token_span()); output.push_literal(literal); } - ValueContent::UnsupportedLiteral(literal) => { + AnyValueContent::UnsupportedLiteral(literal) => { output.extend_raw_tokens(literal.0.to_token_stream()) } - ValueContent::Object(_) => { + AnyValueContent::Object(_) => { return output.type_err("Objects cannot be output to a stream"); } - ValueContent::Array(array) => array.output_items_to(output, Grouping::Flattened)?, - ValueContent::Stream(value) => value.append_cloned_into(output.output_stream), - ValueContent::Iterator(iterator) => iterator + AnyValueContent::Array(array) => array.output_items_to(output, Grouping::Flattened)?, + AnyValueContent::Stream(value) => value.append_cloned_into(output.output_stream), + AnyValueContent::Iterator(iterator) => iterator .clone() .output_items_to(output, Grouping::Flattened)?, - ValueContent::Range(value) => { + AnyValueContent::Range(value) => { let iterator = IteratorValue::new_for_range(value.clone())?; iterator.output_items_to(output, Grouping::Flattened)? } - ValueContent::Parser(_) => { + AnyValueContent::Parser(_) => { return output.type_err("Parsers cannot be output to a stream"); } }; Ok(()) } - pub(crate) fn concat_recursive(&self, behaviour: &ConcatBehaviour) -> ExecutionResult { + pub(crate) fn concat_recursive(self, behaviour: &ConcatBehaviour) -> ExecutionResult { let mut output = String::new(); self.concat_recursive_into(&mut output, behaviour)?; Ok(output) } pub(crate) fn concat_recursive_into( - &self, + self, output: &mut String, behaviour: &ConcatBehaviour, ) -> ExecutionResult<()> { match self { - ValueContent::None(_) => { + AnyValueContent::None(_) => { if behaviour.show_none_values { output.push_str("None"); } } - ValueContent::Stream(stream) => { + AnyValueContent::Stream(stream) => { stream.concat_as_literal_into(output, behaviour); } - ValueContent::Array(array) => { + AnyValueContent::Array(array) => { array.concat_recursive_into(output, behaviour)?; } - ValueContent::Object(object) => { + AnyValueContent::Object(object) => { object.concat_recursive_into(output, behaviour)?; } - ValueContent::Iterator(iterator) => { + AnyValueContent::Iterator(iterator) => { iterator.concat_recursive_into(output, behaviour)?; } - ValueContent::Range(range) => { + AnyValueContent::Range(range) => { range.concat_recursive_into(output, behaviour)?; } - ValueContent::Parser(_) => { + AnyValueContent::Parser(_) => { return behaviour .error_span_range .type_err("Parsers cannot be output to a string"); } - ValueContent::Integer(_) - | ValueContent::Float(_) - | ValueContent::Char(_) - | ValueContent::Bool(_) - | ValueContent::UnsupportedLiteral(_) - | ValueContent::String(_) => { + AnyValueContent::Integer(_) + | AnyValueContent::Float(_) + | AnyValueContent::Char(_) + | AnyValueContent::Bool(_) + | AnyValueContent::UnsupportedLiteral(_) + | AnyValueContent::String(_) => { // This isn't the most efficient, but it's less code and debug doesn't need to be super efficient. let stream = self .output_to_new_stream(Grouping::Flattened, behaviour.error_span_range) @@ -520,6 +480,57 @@ impl Value { } Ok(()) } + + pub(crate) fn index_ref( + self, + access: IndexAccess, + index: Spanned>, + ) -> ExecutionResult<&'a AnyValue> { + match self { + AnyValueContent::Array(array) => array.index_ref(index), + AnyValueContent::Object(object) => object.index_ref(index), + other => access.type_err(format!("Cannot index into {}", other.articled_kind())), + } + } + + pub(crate) fn property_ref(self, access: &PropertyAccess) -> ExecutionResult<&'a AnyValue> { + match self { + AnyValueContent::Object(object) => object.property_ref(access), + other => access.type_err(format!( + "Cannot access properties on {}", + other.articled_kind() + )), + } + } +} + +impl<'a> AnyValueMut<'a> { + pub(crate) fn index_mut( + self, + access: IndexAccess, + index: Spanned, + auto_create: bool, + ) -> ExecutionResult<&'a mut AnyValue> { + match self { + AnyValueContent::Array(array) => array.index_mut(index), + AnyValueContent::Object(object) => object.index_mut(index, auto_create), + other => access.type_err(format!("Cannot index into {}", other.articled_kind())), + } + } + + pub(crate) fn property_mut( + self, + access: &PropertyAccess, + auto_create: bool, + ) -> ExecutionResult<&'a mut AnyValue> { + match self { + AnyValueContent::Object(object) => object.property_mut(access, auto_create), + other => access.type_err(format!( + "Cannot access properties on {}", + other.articled_kind() + )), + } + } } pub(crate) struct ToStreamContext<'a> { @@ -574,17 +585,17 @@ impl HasSpanRange for ToStreamContext<'_> { } } -impl Spanned { +impl Spanned { pub(crate) fn into_stream(self) -> ExecutionResult { let Spanned(value, span_range) = self; - value.0.into_stream(Grouping::Flattened, span_range) + value.into_stream(Grouping::Flattened, span_range) } pub(crate) fn resolve_any_iterator( self, resolution_target: &str, - ) -> ExecutionResult> { - IterableValue::resolve_owned(self, resolution_target)?.try_map(|v| v.into_iterator()) + ) -> ExecutionResult { + IterableValue::resolve_value(self, resolution_target)?.into_iterator() } } diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 02311670..18a41456 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) ArrayType => ValueType(ValueContent::Array), + pub(crate) ArrayType => AnyType(AnyValueContent::Array), content: ArrayValue, kind: pub(crate) ArrayKind, type_name: "array", @@ -21,11 +21,11 @@ define_leaf_type! { #[derive(Clone)] pub(crate) struct ArrayValue { - pub(crate) items: Vec, + pub(crate) items: Vec, } impl ArrayValue { - pub(crate) fn new(items: Vec) -> Self { + pub(crate) fn new(items: Vec) -> Self { Self { items } } @@ -35,22 +35,22 @@ impl ArrayValue { grouping: Grouping, ) -> ExecutionResult<()> { for item in &self.items { - item.output_to(grouping, output)?; + item.as_ref_value().output_to(grouping, output)?; } Ok(()) } pub(super) fn into_indexed( mut self, - Spanned(index, span_range): Spanned<&Value>, - ) -> ExecutionResult { + Spanned(index, span_range): Spanned, + ) -> ExecutionResult { Ok(match index { - ValueContent::Integer(integer) => { + AnyValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; std::mem::replace(&mut self.items[index], ().into_value()) } - ValueContent::Range(range) => { + AnyValueContent::Range(range) => { let range = Spanned(range, span_range).resolve_to_index_range(&self)?; let new_items: Vec<_> = self.items.drain(range).collect(); new_items.into_value() @@ -61,15 +61,15 @@ impl ArrayValue { pub(super) fn index_mut( &mut self, - Spanned(index, span_range): Spanned<&Value>, - ) -> ExecutionResult<&mut Value> { + Spanned(index, span_range): Spanned, + ) -> ExecutionResult<&mut AnyValue> { Ok(match index { - ValueContent::Integer(integer) => { + AnyValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; &mut self.items[index] } - ValueContent::Range(..) => { + AnyValueContent::Range(..) => { // Temporary until we add slice types - we error here return span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]"); } @@ -79,15 +79,15 @@ impl ArrayValue { pub(super) fn index_ref( &self, - Spanned(index, span_range): Spanned<&Value>, - ) -> ExecutionResult<&Value> { + Spanned(index, span_range): Spanned, + ) -> ExecutionResult<&AnyValue> { Ok(match index { - ValueContent::Integer(integer) => { + AnyValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; &self.items[index] } - ValueContent::Range(..) => { + AnyValueContent::Range(..) => { // Temporary until we add slice types - we error here return span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]"); } @@ -97,11 +97,11 @@ impl ArrayValue { pub(super) fn resolve_valid_index( &self, - Spanned(index, span_range): Spanned<&Value>, + Spanned(index, span_range): Spanned, is_exclusive: bool, ) -> ExecutionResult { match index { - ValueContent::Integer(int) => { + AnyValueContent::Integer(int) => { self.resolve_valid_index_from_integer(Spanned(int, span_range), is_exclusive) } _ => span_range.type_err("The index must be an integer"), @@ -110,11 +110,12 @@ impl ArrayValue { fn resolve_valid_index_from_integer( &self, - Spanned(integer, span): Spanned<&IntegerValue>, + Spanned(integer, span): Spanned, is_exclusive: bool, ) -> ExecutionResult { - let index: usize = - Spanned((*integer).into_owned_value(), span).resolve_as("An array index")?; + let index: OptionalSuffix = + Spanned(integer.clone_to_owned_infallible(), span).resolve_as("An array index")?; + let index = index.0; if is_exclusive { if index <= self.items.len() { Ok(index) @@ -169,8 +170,8 @@ impl ValuesEqual for ArrayValue { } } -impl IntoValue for Vec { - fn into_value(self) -> Value { +impl IntoValue for Vec { + fn into_value(self) -> AnyValue { ArrayValue { items: self }.into_value() } } @@ -179,7 +180,7 @@ impl_resolvable_argument_for! { ArrayType, (value, context) -> ArrayValue { match value { - ValueContent::Array(value) => Ok(value), + AnyValueContent::Array(value) => Ok(value), _ => context.err("an array", value), } } diff --git a/src/expressions/values/boolean.rs b/src/expressions/values/boolean.rs index 6711f22f..adce8579 100644 --- a/src/expressions/values/boolean.rs +++ b/src/expressions/values/boolean.rs @@ -3,7 +3,7 @@ use super::*; define_leaf_type! { - pub(crate) BoolType => ValueType(ValueContent::Bool), + pub(crate) BoolType => AnyType(AnyValueContent::Bool), content: bool, kind: pub(crate) BoolKind, type_name: "bool", @@ -159,21 +159,21 @@ define_type_features! { Some(match operation { UnaryOperation::Not { .. } => unary_definitions::not(), UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { - ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - ValueLeafKind::Bool(_) => unary_definitions::cast_to_boolean(), - ValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Bool(_) => unary_definitions::cast_to_boolean(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, @@ -187,7 +187,7 @@ impl_resolvable_argument_for! { BoolType, (value, context) -> bool { match value { - ValueContent::Bool(value) => Ok(value), + AnyValueContent::Bool(value) => Ok(value), other => context.err("a bool", other), } } diff --git a/src/expressions/values/character.rs b/src/expressions/values/character.rs index 426bc529..af15e2e8 100644 --- a/src/expressions/values/character.rs +++ b/src/expressions/values/character.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) CharType => ValueType(ValueContent::Char), + pub(crate) CharType => AnyType(AnyValueContent::Char), content: char, kind: pub(crate) CharKind, type_name: "char", @@ -128,21 +128,21 @@ define_type_features! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { - ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - ValueLeafKind::Char(_) => unary_definitions::cast_to_char(), - ValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Char(_) => unary_definitions::cast_to_char(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, @@ -156,7 +156,7 @@ impl_resolvable_argument_for! { CharType, (value, context) -> char { match value { - ValueContent::Char(char) => Ok(char), + AnyValueContent::Char(char) => Ok(char), _ => context.err("a char", value), } } diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index 1a44325d..3f80c925 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -1,7 +1,7 @@ use super::*; define_parent_type! { - pub(crate) FloatType => ValueType(ValueContent::Float), + pub(crate) FloatType => AnyType(AnyValueContent::Float), content: pub(crate) FloatContent, leaf_kind: pub(crate) FloatLeafKind, type_kind: ParentTypeKind::Float(pub(crate) FloatTypeKind), @@ -111,7 +111,7 @@ fn assign_op( Ok(()) } -impl Debug for FloatValue { +impl Debug for FloatValueRef<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { FloatContent::Untyped(v) => write!(f, "{}", v.into_fallback()), @@ -136,13 +136,14 @@ fn align_types(mut lhs: FloatValue, mut rhs: FloatValue) -> (FloatValue, FloatVa (lhs, rhs) } -// TODO[concepts]: Should really be over FloatRef<'a> -impl ValuesEqual for FloatValue { +impl<'a> ValuesEqual for FloatValueRef<'a> { /// Handles type coercion between typed and untyped floats. /// Uses Rust's float `==`, so `NaN != NaN`. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { // Align types (untyped -> typed conversion) - let (lhs, rhs) = align_types(*self, *other); + let lhs = self.clone_to_owned_infallible(); + let rhs = other.clone_to_owned_infallible(); + let (lhs, rhs) = align_types(lhs, rhs); // After alignment, compare directly. // Each variant has two lines: same-type comparison, then type-mismatch fallback. @@ -356,7 +357,7 @@ impl_resolvable_argument_for! { FloatType, (value, context) -> FloatValue { match value { - Value::Float(value) => Ok(value), + AnyValue::Float(value) => Ok(value), other => context.err("a float", other), } } diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 71431ede..1457f027 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -89,23 +89,23 @@ macro_rules! impl_float_operations { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { - ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - ValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), - ValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), - ValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), - ValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + AnyValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + AnyValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, } _ => return None, @@ -121,7 +121,7 @@ macro_rules! impl_float_operations { fn resolve_type_property( property_name: &str, - ) -> Option { + ) -> Option { match property_name { "MAX" => Some($float_type::MAX.into_value()), "MIN" => Some($float_type::MIN.into_value()), @@ -150,7 +150,7 @@ impl_float_operations!(F32Type mod f32_interface: F32(f32), F64Type mod f64_inte macro_rules! impl_resolvable_float_subtype { ($type_def:ident, $kind:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { define_leaf_type! { - pub(crate) $type_def => FloatType(FloatContent::$variant) => ValueType, + pub(crate) $type_def => FloatType(FloatContent::$variant) => AnyType, content: $type, kind: pub(crate) $kind, type_name: $type_name, @@ -214,37 +214,37 @@ macro_rules! impl_resolvable_float_subtype { } } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - value: Value, + value: AnyValue, context: ResolutionContext, ) -> ExecutionResult { match value { - Value::Float(x) => <$type>::resolve_from_value(x, context), + AnyValue::Float(x) => <$type>::resolve_from_value(x, context), other => context.err($articled_display_name, other), } } } - impl ResolvableShared for $type { + impl ResolvableShared for $type { fn resolve_from_ref<'a>( - value: &'a Value, + value: &'a AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a Self> { match value { - ValueContent::Float(FloatContent::$variant(x)) => Ok(x), + AnyValueContent::Float(FloatContent::$variant(x)) => Ok(x), other => context.err($articled_display_name, other), } } } - impl ResolvableMutable for $type { + impl ResolvableMutable for $type { fn resolve_from_mut<'a>( - value: &'a mut Value, + value: &'a mut AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { match value { - ValueContent::Float(FloatContent::$variant(x)) => Ok(x), + AnyValueContent::Float(FloatContent::$variant(x)) => Ok(x), other => context.err($articled_display_name, other), } } diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index fa34b6f8..cbd1b088 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) UntypedFloatType => FloatType(FloatContent::Untyped) => ValueType, + pub(crate) UntypedFloatType => FloatType(FloatContent::Untyped) => AnyType, content: UntypedFloat, kind: pub(crate) UntypedFloatKind, type_name: "untyped_float", @@ -155,23 +155,23 @@ define_type_features! { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { - ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - ValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), - ValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), - ValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), - ValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + AnyValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + AnyValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, @@ -194,8 +194,11 @@ impl ResolvableArgumentTarget for UntypedFloatFallback { type ValueType = UntypedFloatType; } -impl ResolvableOwned for UntypedFloatFallback { - fn resolve_from_value(input_value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for UntypedFloatFallback { + fn resolve_from_value( + input_value: AnyValue, + context: ResolutionContext, + ) -> ExecutionResult { let value = UntypedFloat::resolve_from_value(input_value, context)?; Ok(UntypedFloatFallback(value.0)) } @@ -214,7 +217,7 @@ impl_resolvable_argument_for! { UntypedFloatType, (value, context) -> UntypedFloat { match value { - ValueContent::Float(FloatContent::Untyped(x)) => Ok(x), + AnyValueContent::Float(FloatContent::Untyped(x)) => Ok(x), other => context.err("an untyped float", other), } } diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index 5bcc3699..863585dd 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -1,7 +1,7 @@ use super::*; define_parent_type! { - pub(crate) IntegerType => ValueType(ValueContent::Integer), + pub(crate) IntegerType => AnyType(AnyValueContent::Integer), content: pub(crate) IntegerContent, leaf_kind: pub(crate) IntegerLeafKind, type_kind: ParentTypeKind::Integer(pub(crate) IntegerTypeKind), @@ -25,6 +25,7 @@ define_parent_type! { } pub(crate) type IntegerValue = IntegerContent<'static, BeOwned>; +pub(crate) type IntegerValueRef<'a> = IntegerContent<'a, BeRef>; impl IntegerValue { pub(super) fn for_litint(lit: &syn::LitInt) -> ParseResult> { @@ -57,10 +58,10 @@ impl IntegerValue { pub(crate) fn resolve_untyped_to_match_other( Spanned(value, span): Spanned, - other: &Value, + other: &AnyValue, ) -> ExecutionResult { match (value, other) { - (IntegerValue::Untyped(this), Value::Integer(other)) => { + (IntegerValue::Untyped(this), AnyValue::Integer(other)) => { this.spanned(span).into_kind(other.kind()) } (value, _) => Ok(value), @@ -112,7 +113,7 @@ impl IntegerValue { } } -impl Debug for IntegerValue { +impl Debug for IntegerValueRef<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Untyped(v) => write!(f, "{}", v.into_fallback()), @@ -132,29 +133,32 @@ impl Debug for IntegerValue { } } -impl IntegerValue { - /// Aligns types for comparison - converts untyped to match the other's type. - /// Returns `None` if the untyped value doesn't fit in the target type. - fn align_types(mut lhs: Self, mut rhs: Self) -> Option<(Self, Self)> { - match (&lhs, &rhs) { - (IntegerValue::Untyped(l), typed) if !matches!(typed, IntegerValue::Untyped(_)) => { - lhs = l.try_into_kind(typed.kind())?; - } - (typed, IntegerValue::Untyped(r)) if !matches!(typed, IntegerValue::Untyped(_)) => { - rhs = r.try_into_kind(lhs.kind())?; - } - _ => {} // Both same type or both untyped - no conversion needed +/// Aligns types for comparison - converts untyped to match the other's type. +/// Returns `None` if the untyped value doesn't fit in the target type. +fn align_types( + mut lhs: IntegerValue, + mut rhs: IntegerValue, +) -> Option<(IntegerValue, IntegerValue)> { + match (&lhs, &rhs) { + (IntegerValue::Untyped(l), typed) if !matches!(typed, IntegerValue::Untyped(_)) => { + lhs = l.try_into_kind(typed.kind())?; } - Some((lhs, rhs)) + (typed, IntegerValue::Untyped(r)) if !matches!(typed, IntegerValue::Untyped(_)) => { + rhs = r.try_into_kind(lhs.kind())?; + } + _ => {} // Both same type or both untyped - no conversion needed } + Some((lhs, rhs)) } -impl ValuesEqual for IntegerValue { +impl<'a> ValuesEqual for IntegerValueRef<'a> { /// Handles type coercion between typed and untyped integers. /// E.g., `5 == 5u32` returns true. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { // Align types (untyped -> typed conversion) - let Some((lhs, rhs)) = Self::align_types(*self, *other) else { + let lhs = self.clone_to_owned_infallible(); + let rhs = other.clone_to_owned_infallible(); + let Some((lhs, rhs)) = align_types(lhs, rhs) else { return ctx.leaf_values_not_equal(self, other); }; @@ -582,7 +586,7 @@ impl_resolvable_argument_for! { IntegerType, (value, context) -> IntegerValue { match value { - Value::Integer(value) => Ok(value), + AnyValue::Integer(value) => Ok(value), other => context.err("an integer", other), } } @@ -594,10 +598,13 @@ impl ResolvableArgumentTarget for CoercedToU32 { type ValueType = IntegerType; } -impl ResolvableOwned for CoercedToU32 { - fn resolve_from_value(input_value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for CoercedToU32 { + fn resolve_from_value( + input_value: AnyValue, + context: ResolutionContext, + ) -> ExecutionResult { let integer = match input_value { - Value::Integer(value) => value, + AnyValue::Integer(value) => value, other => return context.err("an integer", other), }; let coerced = match integer { @@ -617,7 +624,7 @@ impl ResolvableOwned for CoercedToU32 { }; match coerced { Some(value) => Ok(CoercedToU32(value)), - None => context.err("a u32-compatible integer", Value::Integer(integer)), + None => context.err("a u32-compatible integer", AnyValue::Integer(integer)), } } } diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 0fc1beb6..57f9053d 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -110,28 +110,28 @@ macro_rules! impl_int_operations { )? UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { $( - ValueLeafKind::Char(_) => { + AnyValueLeafKind::Char(_) => { ignore_all!($char_cast); // Only include for types with CharCast unary_definitions::cast_to_char() } )? - ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - ValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), - ValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), - ValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), - ValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + AnyValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + AnyValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, } _ => return None, @@ -147,7 +147,7 @@ macro_rules! impl_int_operations { fn resolve_type_property( property_name: &str, - ) -> Option { + ) -> Option { match property_name { "MAX" => Some($integer_type::MAX.into_value()), "MIN" => Some($integer_type::MIN.into_value()), @@ -184,7 +184,7 @@ impl_int_operations!( macro_rules! impl_resolvable_integer_subtype { ($type_def:ident, $kind:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { define_leaf_type! { - pub(crate) $type_def => IntegerType(IntegerContent::$variant) => ValueType, + pub(crate) $type_def => IntegerType(IntegerContent::$variant) => AnyType, content: $type, kind: pub(crate) $kind, type_name: $type_name, @@ -248,37 +248,37 @@ macro_rules! impl_resolvable_integer_subtype { } } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - value: Value, + value: AnyValue, context: ResolutionContext, ) -> ExecutionResult { match value { - Value::Integer(x) => <$type>::resolve_from_value(x, context), + AnyValue::Integer(x) => <$type>::resolve_from_value(x, context), other => context.err($articled_display_name, other), } } } - impl ResolvableShared for $type { + impl ResolvableShared for $type { fn resolve_from_ref<'a>( - value: &'a Value, + value: &'a AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a Self> { match value { - Value::Integer(IntegerValue::$variant(x)) => Ok(x), + AnyValue::Integer(IntegerValue::$variant(x)) => Ok(x), other => context.err($articled_display_name, other), } } } - impl ResolvableMutable for $type { + impl ResolvableMutable for $type { fn resolve_from_mut<'a>( - value: &'a mut Value, + value: &'a mut AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { match value { - Value::Integer(IntegerValue::$variant(x)) => Ok(x), + AnyValue::Integer(IntegerValue::$variant(x)) => Ok(x), other => context.err($articled_display_name, other), } } diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index e6892834..185350b7 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) UntypedIntegerType => IntegerType(IntegerContent::Untyped) => ValueType, + pub(crate) UntypedIntegerType => IntegerType(IntegerContent::Untyped) => AnyType, content: UntypedInteger, kind: pub(crate) UntypedIntegerKind, type_name: "untyped_int", @@ -211,23 +211,23 @@ define_type_features! { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { - ValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), - ValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), - ValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), - ValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), - ValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), - ValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), - ValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), - ValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), - ValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), - ValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), - ValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), - ValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), - ValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), - ValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), - ValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), - ValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), - ValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + AnyValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + AnyValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, @@ -250,10 +250,13 @@ impl ResolvableArgumentTarget for UntypedIntegerFallback { type ValueType = UntypedIntegerType; } -impl ResolvableOwned for UntypedIntegerFallback { - fn resolve_from_value(input_value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for UntypedIntegerFallback { + fn resolve_from_value( + input_value: AnyValue, + context: ResolutionContext, + ) -> ExecutionResult { let value: UntypedInteger = - ResolvableOwned::::resolve_from_value(input_value, context)?; + ResolvableOwned::::resolve_from_value(input_value, context)?; Ok(UntypedIntegerFallback(value.into_fallback())) } } @@ -274,7 +277,7 @@ impl_resolvable_argument_for! { UntypedIntegerType, (value, context) -> UntypedInteger { match value { - Value::Integer(IntegerValue::Untyped(x)) => Ok(x), + AnyValue::Integer(IntegerValue::Untyped(x)) => Ok(x), _ => context.err("an untyped integer", value), } } diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index c210eff6..6da89aa1 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -30,15 +30,15 @@ impl ResolvableArgumentTarget for IterableValue { type ValueType = IterableType; } -impl ResolvableOwned for IterableValue { - fn resolve_from_value(value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for IterableValue { + fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { Ok(match value { - Value::Array(x) => Self::Array(x), - Value::Object(x) => Self::Object(x), - Value::Stream(x) => Self::Stream(x), - Value::Range(x) => Self::Range(x), - Value::Iterator(x) => Self::Iterator(x), - Value::String(x) => Self::String(x), + AnyValue::Array(x) => Self::Array(x), + AnyValue::Object(x) => Self::Object(x), + AnyValue::Stream(x) => Self::Stream(x), + AnyValue::Range(x) => Self::Range(x), + AnyValue::Iterator(x) => Self::Iterator(x), + AnyValue::String(x) => Self::String(x), _ => { return context.err( "an iterable (iterator, array, object, stream, range or string)", @@ -75,11 +75,11 @@ define_type_features! { ZipIterators::new_from_iterator(iterator, context.span_range())?.run_zip(context.interpreter, false) } - fn intersperse(this: IterableValue, separator: Value, settings: Option) -> ExecutionResult { + fn intersperse(this: IterableValue, separator: AnyValue, settings: Option) -> ExecutionResult { run_intersperse(this, separator, settings.unwrap_or_default()) } - [context] fn to_vec(this: IterableValue) -> ExecutionResult> { + [context] fn to_vec(this: IterableValue) -> ExecutionResult> { let error_span_range = context.span_range(); let mut counter = context.interpreter.start_iteration_counter(&error_span_range); let iterator = this.into_iterator()?; @@ -105,7 +105,7 @@ define_type_features! { interface_items { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { - UnaryOperation::Cast { target: CastTarget(ValueLeafKind::Iterator(_)), .. } =>{ + UnaryOperation::Cast { target: CastTarget(AnyValueLeafKind::Iterator(_)), .. } =>{ unary_definitions::cast_into_iterator() } _ => return None, @@ -143,14 +143,20 @@ impl IsArgument for IterableRef<'static> { fn from_argument(argument: Spanned) -> ExecutionResult { Ok(match argument.kind() { - ValueLeafKind::Iterator(_) => { + AnyValueLeafKind::Iterator(_) => { IterableRef::Iterator(IsArgument::from_argument(argument)?) } - ValueLeafKind::Array(_) => IterableRef::Array(IsArgument::from_argument(argument)?), - ValueLeafKind::Stream(_) => IterableRef::Stream(IsArgument::from_argument(argument)?), - ValueLeafKind::Range(_) => IterableRef::Range(IsArgument::from_argument(argument)?), - ValueLeafKind::Object(_) => IterableRef::Object(IsArgument::from_argument(argument)?), - ValueLeafKind::String(_) => IterableRef::String(IsArgument::from_argument(argument)?), + AnyValueLeafKind::Array(_) => IterableRef::Array(IsArgument::from_argument(argument)?), + AnyValueLeafKind::Stream(_) => { + IterableRef::Stream(IsArgument::from_argument(argument)?) + } + AnyValueLeafKind::Range(_) => IterableRef::Range(IsArgument::from_argument(argument)?), + AnyValueLeafKind::Object(_) => { + IterableRef::Object(IsArgument::from_argument(argument)?) + } + AnyValueLeafKind::String(_) => { + IterableRef::String(IsArgument::from_argument(argument)?) + } _ => { return argument.type_err( "Expected iterable (iterator, array, object, stream, range or string)", diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index c70ab855..6f16d336 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) IteratorType => ValueType(ValueContent::Iterator), + pub(crate) IteratorType => AnyType(AnyValueContent::Iterator), content: IteratorValue, kind: pub(crate) IteratorKind, type_name: "iterator", @@ -30,7 +30,7 @@ impl IteratorValue { } #[allow(unused)] - pub(crate) fn new_any(iterator: impl Iterator + 'static + Clone) -> Self { + pub(crate) fn new_any(iterator: impl Iterator + 'static + Clone) -> Self { Self::new_custom(Box::new(iterator)) } @@ -72,11 +72,11 @@ impl IteratorValue { Self::new_vec(iterator) } - fn new_vec(iterator: std::vec::IntoIter) -> Self { + fn new_vec(iterator: std::vec::IntoIter) -> Self { Self::new(IteratorValueInner::Vec(Box::new(iterator))) } - pub(crate) fn new_custom(iterator: Box>) -> Self { + pub(crate) fn new_custom(iterator: Box>) -> Self { Self::new(IteratorValueInner::Other(iterator)) } @@ -89,7 +89,7 @@ impl IteratorValue { } } - pub(crate) fn singleton_value(mut self) -> Option { + pub(crate) fn singleton_value(mut self) -> Option { let first = self.next()?; if self.next().is_none() { Some(first) @@ -108,7 +108,7 @@ impl IteratorValue { if i > LIMIT { return output.debug_err(format!("Only a maximum of {} items can be output to a stream from an iterator, to protect you from infinite loops. This can't currently be reconfigured with the iteration limit.", LIMIT)); } - item.output_to(grouping, output)?; + item.as_ref_value().output_to(grouping, output)?; } Ok(()) } @@ -129,7 +129,7 @@ impl IteratorValue { ) } - pub(crate) fn any_iterator_to_string>( + pub(crate) fn any_iterator_to_string>( iterator: impl Iterator, output: &mut String, behaviour: &ConcatBehaviour, @@ -170,7 +170,8 @@ impl IteratorValue { if i != 0 && behaviour.add_space_between_token_trees { output.push(' '); } - item.concat_recursive_into(output, behaviour)?; + item.as_ref_value() + .concat_recursive_into(output, behaviour)?; } if behaviour.output_literal_structure { if is_empty { @@ -184,14 +185,14 @@ impl IteratorValue { } impl IntoValue for IteratorValueInner { - fn into_value(self) -> Value { - Value::Iterator(IteratorValue::new(self)) + fn into_value(self) -> AnyValue { + AnyValue::Iterator(IteratorValue::new(self)) } } -impl IntoValue for Box> { - fn into_value(self) -> Value { - Value::Iterator(IteratorValue::new_custom(self)) +impl IntoValue for Box> { + fn into_value(self) -> AnyValue { + AnyValue::Iterator(IteratorValue::new_custom(self)) } } @@ -199,7 +200,7 @@ impl_resolvable_argument_for! { IteratorType, (value, context) -> IteratorValue { match value { - Value::Iterator(value) => Ok(value), + AnyValue::Iterator(value) => Ok(value), _ => context.err("an iterator", value), } } @@ -247,13 +248,13 @@ impl ValuesEqual for IteratorValue { #[derive(Clone)] enum IteratorValueInner { // We Box these so that Value is smaller on the stack - Vec(Box< as IntoIterator>::IntoIter>), + Vec(Box< as IntoIterator>::IntoIter>), Stream(Box<::IntoIter>), - Other(Box>), + Other(Box>), } impl Iterator for IteratorValue { - type Item = Value; + type Item = AnyValue; fn next(&mut self) -> Option { match &mut self.iterator { @@ -277,7 +278,7 @@ impl Iterator for IteratorValue { } impl Iterator for Mutable { - type Item = Value; + type Item = AnyValue; fn next(&mut self) -> Option { let this: &mut IteratorValue = &mut *self; @@ -294,7 +295,7 @@ define_type_features! { impl IteratorType, pub(crate) mod iterator_interface { pub(crate) mod methods { - fn next(mut this: Mutable) -> Value { + fn next(mut this: Mutable) -> AnyValue { match this.next() { Some(value) => value, None => ().into_value(), diff --git a/src/expressions/values/mod.rs b/src/expressions/values/mod.rs index 3bdf28b8..14fb0485 100644 --- a/src/expressions/values/mod.rs +++ b/src/expressions/values/mod.rs @@ -1,3 +1,4 @@ +mod any_value; mod array; mod boolean; mod character; @@ -16,8 +17,8 @@ mod range; mod stream; mod string; mod unsupported_literal; -mod value; +pub(crate) use any_value::*; pub(crate) use array::*; pub(crate) use boolean::*; pub(crate) use character::*; @@ -36,7 +37,6 @@ pub(crate) use range::*; pub(crate) use stream::*; pub(crate) use string::*; pub(crate) use unsupported_literal::*; -pub(crate) use value::*; // Marked as use for sub-modules to use with a `use super::*` statement use super::*; diff --git a/src/expressions/values/none.rs b/src/expressions/values/none.rs index 473a2f45..1984a6b1 100644 --- a/src/expressions/values/none.rs +++ b/src/expressions/values/none.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) NoneType => ValueType(ValueContent::None), + pub(crate) NoneType => AnyType(AnyValueContent::None), content: (), kind: pub(crate) NoneKind, type_name: "none", @@ -14,10 +14,10 @@ impl ResolvableArgumentTarget for () { type ValueType = NoneType; } -impl ResolvableOwned for () { - fn resolve_from_value(value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for () { + fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { match value { - Value::None(_) => Ok(()), + AnyValue::None(_) => Ok(()), other => context.err("None", other), } } diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index 557dcbe4..acf9b4f5 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) ObjectType => ValueType(ValueContent::Object), + pub(crate) ObjectType => AnyType(AnyValueContent::Object), content: ObjectValue, kind: pub(crate) ObjectKind, type_name: "object", @@ -28,7 +28,7 @@ impl_resolvable_argument_for! { ObjectType, (value, context) -> ObjectValue { match value { - Value::Object(value) => Ok(value), + AnyValue::Object(value) => Ok(value), _ => context.err("an object", value), } } @@ -38,28 +38,28 @@ impl_resolvable_argument_for! { pub(crate) struct ObjectEntry { #[allow(unused)] pub(crate) key_span: Span, - pub(crate) value: Value, + pub(crate) value: AnyValue, } impl ObjectValue { - pub(super) fn into_indexed(mut self, index: Spanned<&Value>) -> ExecutionResult { - let key = index.resolve_as("An object key")?; + pub(super) fn into_indexed(mut self, index: Spanned) -> ExecutionResult { + let key = index.downcast_resolve("An object key")?; Ok(self.remove_or_none(key)) } - pub(super) fn into_property(mut self, access: &PropertyAccess) -> ExecutionResult { + pub(super) fn into_property(mut self, access: &PropertyAccess) -> ExecutionResult { let key = access.property.to_string(); Ok(self.remove_or_none(&key)) } - pub(crate) fn remove_or_none(&mut self, key: &str) -> Value { + pub(crate) fn remove_or_none(&mut self, key: &str) -> AnyValue { match self.entries.remove(key) { Some(entry) => entry.value, None => ().into_value(), } } - pub(crate) fn remove_no_none(&mut self, key: &str) -> Option { + pub(crate) fn remove_no_none(&mut self, key: &str) -> Option { match self.entries.remove(key) { Some(entry) => { if entry.value.is_none() { @@ -74,15 +74,15 @@ impl ObjectValue { pub(super) fn index_mut( &mut self, - index: Spanned<&Value>, + index: Spanned, auto_create: bool, - ) -> ExecutionResult<&mut Value> { - let index: Spanned<&str> = index.resolve_as("An object key")?; + ) -> ExecutionResult<&mut AnyValue> { + let index: Spanned<&str> = index.downcast_resolve_spanned("An object key")?; self.mut_entry(index.map(|s| s.to_string()), auto_create) } - pub(super) fn index_ref(&self, index: Spanned<&Value>) -> ExecutionResult<&Value> { - let key: Spanned<&str> = index.resolve_as("An object key")?; + pub(super) fn index_ref(&self, index: Spanned) -> ExecutionResult<&AnyValue> { + let key: Spanned<&str> = index.downcast_resolve_spanned("An object key")?; let entry = self.entries.get(*key).ok_or_else(|| { key.value_error(format!("The object does not have a field named `{}`", *key)) })?; @@ -93,14 +93,14 @@ impl ObjectValue { &mut self, access: &PropertyAccess, auto_create: bool, - ) -> ExecutionResult<&mut Value> { + ) -> ExecutionResult<&mut AnyValue> { self.mut_entry( access.property.to_string().spanned(access.property.span()), auto_create, ) } - pub(super) fn property_ref(&self, access: &PropertyAccess) -> ExecutionResult<&Value> { + pub(super) fn property_ref(&self, access: &PropertyAccess) -> ExecutionResult<&AnyValue> { let key = access.property.to_string(); let entry = self.entries.get(&key).ok_or_else(|| { access.value_error(format!("The object does not have a field named `{}`", key)) @@ -112,7 +112,7 @@ impl ObjectValue { &mut self, Spanned(key, key_span): Spanned, auto_create: bool, - ) -> ExecutionResult<&mut Value> { + ) -> ExecutionResult<&mut AnyValue> { use std::collections::btree_map::*; Ok(match self.entries.entry(key) { Entry::Occupied(entry) => &mut entry.into_mut().value, @@ -171,7 +171,10 @@ impl ObjectValue { if behaviour.add_space_between_token_trees { output.push(' '); } - entry.value.concat_recursive_into(output, behaviour)?; + entry + .value + .as_ref_value() + .concat_recursive_into(output, behaviour)?; is_first = false; } if behaviour.output_literal_structure { @@ -215,7 +218,7 @@ impl Spanned<&ObjectValue> { match self.entries.get(field_name) { None | Some(ObjectEntry { - value: Value::None(_), + value: AnyValue::None(_), .. }) => { missing_fields.push(field_name); @@ -256,8 +259,8 @@ impl Spanned<&ObjectValue> { } impl IntoValue for BTreeMap { - fn into_value(self) -> Value { - Value::Object(ObjectValue { entries: self }) + fn into_value(self) -> AnyValue { + AnyValue::Object(ObjectValue { entries: self }) } } diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 6c9cecb8..55e8c243 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) ParserType => ValueType(ValueContent::Parser), + pub(crate) ParserType => AnyType(AnyValueContent::Parser), content: ParserHandle, kind: pub(crate) ParserKind, type_name: "parser", @@ -193,9 +193,9 @@ define_type_features! { Ok(OutputStream::new_with(|s| s.push_literal(literal))) } - [context] fn inferred_literal(this: Spanned>) -> ExecutionResult { + [context] fn inferred_literal(this: Spanned>) -> ExecutionResult { let literal = parser(this, context)?.parse()?; - Ok(Value::for_literal(literal).into_value()) + Ok(AnyValue::for_literal(literal).into_value()) } [context] fn is_char(this: Spanned>) -> ExecutionResult { @@ -266,32 +266,32 @@ impl_resolvable_argument_for! { ParserType, (value, context) -> ParserHandle { match value { - Value::Parser(value) => Ok(value), + AnyValue::Parser(value) => Ok(value), other => context.err("a parser", other), } } } impl IntoValue for TokenTree { - fn into_value(self) -> Value { + fn into_value(self) -> AnyValue { OutputStream::new_with(|s| s.push_raw_token_tree(self)).into_value() } } impl IntoValue for Ident { - fn into_value(self) -> Value { + fn into_value(self) -> AnyValue { OutputStream::new_with(|s| s.push_ident(self)).into_value() } } impl IntoValue for Punct { - fn into_value(self) -> Value { + fn into_value(self) -> AnyValue { OutputStream::new_with(|s| s.push_punct(self)).into_value() } } impl IntoValue for Literal { - fn into_value(self) -> Value { + fn into_value(self) -> AnyValue { OutputStream::new_with(|s| s.push_literal(self)).into_value() } } diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index 551c737e..3cbfe4ef 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -3,7 +3,7 @@ use syn::RangeLimits; use super::*; define_leaf_type! { - pub(crate) RangeType => ValueType(ValueContent::Range), + pub(crate) RangeType => AnyType(AnyValueContent::Range), content: RangeValue, kind: pub(crate) RangeKind, type_name: "range", @@ -53,19 +53,27 @@ impl RangeValue { end_exclusive, .. } => { - start_inclusive.concat_recursive_into(output, behaviour)?; + start_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; output.push_str(".."); - end_exclusive.concat_recursive_into(output, behaviour)?; + end_exclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; } RangeValueInner::RangeFrom { start_inclusive, .. } => { - start_inclusive.concat_recursive_into(output, behaviour)?; + start_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; output.push_str(".."); } RangeValueInner::RangeTo { end_exclusive, .. } => { output.push_str(".."); - end_exclusive.concat_recursive_into(output, behaviour)?; + end_exclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; } RangeValueInner::RangeFull { .. } => { output.push_str(".."); @@ -75,13 +83,19 @@ impl RangeValue { end_inclusive, .. } => { - start_inclusive.concat_recursive_into(output, behaviour)?; + start_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; output.push_str("..="); - end_inclusive.concat_recursive_into(output, behaviour)?; + end_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; } RangeValueInner::RangeToInclusive { end_inclusive, .. } => { output.push_str("..="); - end_inclusive.concat_recursive_into(output, behaviour)?; + end_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; } } Ok(()) @@ -102,18 +116,26 @@ impl Spanned<&RangeValue> { end_exclusive, .. } => { - start = array.resolve_valid_index(Spanned(start_inclusive, span_range), false)?; - end = array.resolve_valid_index(Spanned(end_exclusive, span_range), true)?; + start = array.resolve_valid_index( + Spanned(start_inclusive.as_ref_value(), span_range), + false, + )?; + end = array + .resolve_valid_index(Spanned(end_exclusive.as_ref_value(), span_range), true)?; start..end } RangeValueInner::RangeFrom { start_inclusive, .. } => { - start = array.resolve_valid_index(Spanned(start_inclusive, span_range), false)?; + start = array.resolve_valid_index( + Spanned(start_inclusive.as_ref_value(), span_range), + false, + )?; start..array.items.len() } RangeValueInner::RangeTo { end_exclusive, .. } => { - end = array.resolve_valid_index(Spanned(end_exclusive, span_range), true)?; + end = array + .resolve_valid_index(Spanned(end_exclusive.as_ref_value(), span_range), true)?; start..end } RangeValueInner::RangeFull { .. } => start..end, @@ -122,14 +144,23 @@ impl Spanned<&RangeValue> { end_inclusive, .. } => { - start = array.resolve_valid_index(Spanned(start_inclusive, span_range), false)?; + start = array.resolve_valid_index( + Spanned(start_inclusive.as_ref_value(), span_range), + false, + )?; // +1 is safe because it must be < array length. - end = array.resolve_valid_index(Spanned(end_inclusive, span_range), false)? + 1; + end = array.resolve_valid_index( + Spanned(end_inclusive.as_ref_value(), span_range), + false, + )? + 1; start..end } RangeValueInner::RangeToInclusive { end_inclusive, .. } => { // +1 is safe because it must be < array length. - end = array.resolve_valid_index(Spanned(end_inclusive, span_range), false)? + 1; + end = array.resolve_valid_index( + Spanned(end_inclusive.as_ref_value(), span_range), + false, + )? + 1; start..end } }) @@ -253,32 +284,32 @@ impl ValuesEqual for RangeValue { pub(crate) enum RangeValueInner { /// `start .. end` Range { - start_inclusive: Value, + start_inclusive: AnyValue, token: Token![..], - end_exclusive: Value, + end_exclusive: AnyValue, }, /// `start ..` RangeFrom { - start_inclusive: Value, + start_inclusive: AnyValue, token: Token![..], }, /// `.. end` RangeTo { token: Token![..], - end_exclusive: Value, + end_exclusive: AnyValue, }, /// `..` (used inside arrays) RangeFull { token: Token![..] }, /// `start ..= end` RangeInclusive { - start_inclusive: Value, + start_inclusive: AnyValue, token: Token![..=], - end_inclusive: Value, + end_inclusive: AnyValue, }, /// `..= end` RangeToInclusive { token: Token![..=], - end_inclusive: Value, + end_inclusive: AnyValue, }, } @@ -294,7 +325,7 @@ impl RangeValueInner { } } - pub(super) fn into_iterable(self) -> ExecutionResult> { + pub(super) fn into_iterable(self) -> ExecutionResult> { Ok(match self { Self::Range { start_inclusive, @@ -342,8 +373,8 @@ impl RangeValueInner { } impl IntoValue for RangeValueInner { - fn into_value(self) -> Value { - Value::Range(RangeValue { + fn into_value(self) -> AnyValue { + AnyValue::Range(RangeValue { inner: Box::new(self), }) } @@ -353,7 +384,7 @@ impl_resolvable_argument_for! { RangeType, (value, context) -> RangeValue { match value { - Value::Range(value) => Ok(value), + AnyValue::Range(value) => Ok(value), _ => context.err("a range", value), } } @@ -400,11 +431,11 @@ pub(super) enum IterableRangeOf { }, } -fn resolve_range + ResolvableRange>( +fn resolve_range + ResolvableRange>( start: T, dots: syn::RangeLimits, end: Option>, -) -> ExecutionResult>> { +) -> ExecutionResult>> { let definition = match (end, dots) { (Some(end), dots) => { let end = end.resolve_as("The end of this range bound")?; @@ -421,13 +452,13 @@ fn resolve_range + ResolvableRange>( trait ResolvableRange: Sized { fn resolve( definition: IterableRangeOf, - ) -> ExecutionResult>>; + ) -> ExecutionResult>>; } -impl IterableRangeOf { +impl IterableRangeOf { pub(super) fn resolve_iterator( self, - ) -> ExecutionResult>> { + ) -> ExecutionResult>> { let (start, dots, end) = match self { Self::RangeFromTo { start, dots, end } => ( start, @@ -437,7 +468,7 @@ impl IterableRangeOf { Self::RangeFrom { start, dots } => (start, RangeLimits::HalfOpen(dots), None), }; match start { - Value::Integer(mut start) => { + AnyValue::Integer(mut start) => { if let Some(end) = &end { start = IntegerValue::resolve_untyped_to_match_other( start.spanned(dots.span_range()), @@ -460,7 +491,7 @@ impl IterableRangeOf { IntegerValue::Isize(start) => resolve_range(start, dots, end), } } - Value::Char(start) => resolve_range(start, dots, end), + AnyValue::Char(start) => resolve_range(start, dots, end), _ => dots.value_err("The range must be between two integers or two characters"), } } @@ -469,7 +500,7 @@ impl IterableRangeOf { impl ResolvableRange for UntypedInteger { fn resolve( definition: IterableRangeOf, - ) -> ExecutionResult>> { + ) -> ExecutionResult>> { match definition { IterableRangeOf::RangeFromTo { start, dots, end } => { let start = start.into_fallback(); @@ -498,7 +529,7 @@ macro_rules! define_range_resolvers { $($the_type:ident),* $(,)? ) => {$( impl ResolvableRange for $the_type { - fn resolve(definition: IterableRangeOf) -> ExecutionResult>> { + fn resolve(definition: IterableRangeOf) -> ExecutionResult>> { match definition { IterableRangeOf::RangeFromTo { start, dots, end } => { Ok(match dots { diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index e8deed1e..ba159e28 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) StreamType => ValueType(ValueContent::Stream), + pub(crate) StreamType => AnyType(AnyValueContent::Stream), content: OutputStream, kind: pub(crate) StreamKind, type_name: "stream", @@ -98,7 +98,7 @@ impl ValuesEqual for OutputStream { } impl IntoValue for TokenStream { - fn into_value(self) -> Value { + fn into_value(self) -> AnyValue { OutputStream::raw(self).into_value() } } @@ -107,7 +107,7 @@ impl_resolvable_argument_for! { StreamType, (value, context) -> OutputStream { match value { - ValueContent::Stream(value) => Ok(value), + AnyValueContent::Stream(value) => Ok(value), _ => context.err("a stream", value), } } @@ -137,7 +137,7 @@ define_type_features! { OutputStream::raw(this.to_token_stream_removing_any_transparent_groups()) } - fn infer(this: OutputStream) -> ExecutionResult { + fn infer(this: OutputStream) -> ExecutionResult { Ok(this.coerce_into_value()) } @@ -169,10 +169,10 @@ define_type_features! { } // Some literals become Value::UnsupportedLiteral but can still be round-tripped back to a stream - [context] fn to_literal(this: Spanned>) -> ExecutionResult { + [context] fn to_literal(this: Spanned>) -> ExecutionResult { let string = this.concat_content(&ConcatBehaviour::literal(this.span_range())); let literal = string_interface::methods::to_literal(context, string.as_str().into_spanned_ref(this.span_range()))?; - Ok(Value::for_literal(literal).into_value()) + Ok(AnyValue::for_literal(literal).into_value()) } // CORE METHODS @@ -203,10 +203,8 @@ define_type_features! { } } - fn assert_eq(this: Shared, lhs: Spanned>, rhs: Spanned>, message: Option>) -> ExecutionResult<()> { - let lhs_value: &Value = &lhs; - let rhs_value: &Value = &rhs; - match Value::debug_eq(lhs_value, rhs_value) { + fn assert_eq(this: Shared, lhs: Spanned>, rhs: Spanned>, message: Option>) -> ExecutionResult<()> { + match AnyValueRef::debug_eq(&lhs.as_ref_value(), &rhs.as_ref_value()) { Ok(()) => Ok(()), Err(debug_error) => { let error_span_range = this.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); @@ -215,8 +213,8 @@ define_type_features! { None => format!( "Assertion failed: {}\n lhs = {}\n rhs = {}", debug_error.format_message(), - lhs.concat_recursive(&ConcatBehaviour::debug(lhs.span_range()))?, - rhs.concat_recursive(&ConcatBehaviour::debug(rhs.span_range()))?, + lhs.as_ref_value().concat_recursive(&ConcatBehaviour::debug(lhs.span_range()))?, + rhs.as_ref_value().concat_recursive(&ConcatBehaviour::debug(rhs.span_range()))?, ), }; error_span_range.assertion_err(message) @@ -250,7 +248,7 @@ define_type_features! { [context] fn cast_coerced_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { let this = this.into_inner(); let coerced = this.coerce_into_value(); - if let Value::Stream(_) = &coerced { + if let AnyValue::Stream(_) = &coerced { return span.value_err("The stream could not be coerced into a single value"); } // Re-run the cast operation on the coerced value @@ -546,7 +544,7 @@ impl Interpret for ConcatenatedStreamLiteral { string_to_literal(str, self, ident_span)?.into_value() } }; - value.output_to( + value.as_ref_value().output_to( Grouping::Flattened, &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), ) diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index 1d5b050f..44bc0f92 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) StringType => ValueType(ValueContent::String), + pub(crate) StringType => AnyType(AnyValueContent::String), content: String, kind: pub(crate) StringKind, type_name: "string", @@ -21,6 +21,17 @@ define_leaf_type! { }, } +impl<'a> IsValueContent<'a> for &'a str { + type Type = StringType; + type Form = BeRef; +} + +impl<'a> FromValueContent<'a> for &'a str { + fn from_content(value: &'a String) -> Self { + value.as_str() + } +} + impl ValuesEqual for String { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { if self == other { @@ -32,7 +43,7 @@ impl ValuesEqual for String { } impl IntoValue for &str { - fn into_value(self) -> Value { + fn into_value(self) -> AnyValue { self.to_string().into_value() } } @@ -180,7 +191,7 @@ define_type_features! { Some(match operation { UnaryOperation::Neg { .. } | UnaryOperation::Not { .. } => return None, UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { - ValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, }) @@ -209,7 +220,7 @@ impl_resolvable_argument_for! { StringType, (value, context) -> String { match value { - ValueContent::String(value) => Ok(value), + AnyValueContent::String(value) => Ok(value), _ => context.err("a string", value), } } @@ -219,13 +230,13 @@ impl ResolvableArgumentTarget for str { type ValueType = StringType; } -impl ResolvableShared for str { +impl ResolvableShared for str { fn resolve_from_ref<'a>( - value: &'a Value, + value: &'a AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a Self> { match value { - ValueContent::String(s) => Ok(s.as_str()), + AnyValueContent::String(s) => Ok(s.as_str()), _ => context.err("a string", value), } } diff --git a/src/expressions/values/unsupported_literal.rs b/src/expressions/values/unsupported_literal.rs index 5e498cb1..071a4b8e 100644 --- a/src/expressions/values/unsupported_literal.rs +++ b/src/expressions/values/unsupported_literal.rs @@ -1,7 +1,7 @@ use super::*; define_leaf_type! { - pub(crate) UnsupportedLiteralType => ValueType(ValueContent::UnsupportedLiteral), + pub(crate) UnsupportedLiteralType => AnyType(AnyValueContent::UnsupportedLiteral), content: UnsupportedLiteral, kind: pub(crate) UnsupportedLiteralKind, type_name: "unsupported_literal", diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index 168d81c7..cf2104d2 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -6,12 +6,12 @@ use std::rc::Rc; pub(super) enum VariableState { Uninitialized, - Value(Rc>), + Value(Rc>), Finished, } impl VariableState { - pub(crate) fn define(&mut self, value: Value) { + pub(crate) fn define(&mut self, value: AnyValue) { match self { content @ VariableState::Uninitialized => { *content = VariableState::Value(Rc::new(RefCell::new(value))); @@ -119,7 +119,7 @@ impl VariableState { #[derive(Clone)] pub(crate) struct VariableBinding { - data: Rc>, + data: Rc>, variable_span: Span, } @@ -231,7 +231,7 @@ impl LateBoundValue { }) } - pub(crate) fn as_value(&self) -> &Value { + pub(crate) fn as_value(&self) -> &AnyValue { match self { LateBoundValue::Owned(owned) => &owned.owned, LateBoundValue::CopyOnWrite(cow) => cow.as_ref(), @@ -242,14 +242,14 @@ impl LateBoundValue { } impl Deref for LateBoundValue { - type Target = Value; + type Target = AnyValue; fn deref(&self) -> &Self::Target { self.as_value() } } -pub(crate) type OwnedValue = Owned; +pub(crate) type OwnedValue = Owned; /// A semantic wrapper for floating owned values. /// @@ -286,14 +286,14 @@ impl Spanned { pub(crate) fn into_statement_result(self) -> ExecutionResult<()> { let Spanned(value, span_range) = self; match value.0 { - ValueContent::None(_) => Ok(()), + AnyValueContent::None(_) => Ok(()), _ => span_range.control_flow_err("A non-returning statement must not return a value. If you wish to explicitly discard the expression's result, use `let _ = ...;`. Alternatively, If you wish to output the value into the parent token stream, use `emit ...;`"), } } } impl Owned { - pub(crate) fn into_value(self) -> Value { + pub(crate) fn into_value(self) -> AnyValue { self.0.into_value() } @@ -302,7 +302,7 @@ impl Owned { } } -impl From for Value { +impl From for AnyValue { fn from(value: OwnedValue) -> Self { value.0 } @@ -322,8 +322,8 @@ impl DerefMut for Owned { } } -pub(crate) type MutableValue = Mutable; -pub(crate) type AssigneeValue = Assignee; +pub(crate) type MutableValue = Mutable; +pub(crate) type AssigneeValue = Assignee; /// A binding of a unique (mutable) reference to a value. /// See [`ArgumentOwnership::Assignee`] for more details. @@ -356,7 +356,7 @@ impl DerefMut for Assignee { /// Can be destructured as: `Mutable(cell): Mutable` /// /// If you need span information, wrap with `Spanned>`. -pub(crate) struct Mutable(pub(crate) MutableSubRcRefCell); +pub(crate) struct Mutable(pub(crate) MutableSubRcRefCell); impl Mutable { pub(crate) fn into_shared(self) -> Shared { @@ -382,7 +382,7 @@ impl Mutable { pub(crate) static MUTABLE_ERROR_MESSAGE: &str = "The variable cannot be modified as it is already being modified"; -impl Spanned<&mut Mutable> { +impl Spanned<&mut Mutable> { /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -402,14 +402,14 @@ impl Spanned<&mut Mutable> { } } -impl Spanned> { +impl Spanned> { pub(crate) fn transparent_clone(&self) -> ExecutionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; Ok(Owned(value)) } } -impl Mutable { +impl Mutable { pub(crate) fn new_from_owned(value: OwnedValue) -> Self { // Unwrap is safe because it's a new refcell Mutable(MutableSubRcRefCell::new(Rc::new(RefCell::new(value.0))).unwrap()) @@ -448,14 +448,14 @@ impl DerefMut for Mutable { } } -pub(crate) type SharedValue = Shared; +pub(crate) type SharedValue = Shared; /// A simple wrapper for a shared (immutable) reference to a value. /// /// Can be destructured as: `Shared(cell): Shared` /// /// If you need span information, wrap with `Spanned>`. -pub(crate) struct Shared(pub(crate) SharedSubRcRefCell); +pub(crate) struct Shared(pub(crate) SharedSubRcRefCell); #[allow(unused)] impl Shared { @@ -478,7 +478,7 @@ impl Shared { pub(crate) static SHARED_ERROR_MESSAGE: &str = "The variable cannot be read as it is already being modified"; -impl Spanned<&mut Shared> { +impl Spanned<&mut Shared> { /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -498,14 +498,14 @@ impl Spanned<&mut Shared> { } } -impl Spanned> { +impl Spanned> { pub(crate) fn transparent_clone(&self) -> ExecutionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; Ok(Owned(value)) } } -impl Shared { +impl Shared { pub(crate) fn new_from_owned(value: OwnedValue) -> Self { // Unwrap is safe because it's a new refcell Shared(SharedSubRcRefCell::new(Rc::new(RefCell::new(value.0))).unwrap()) @@ -625,7 +625,7 @@ impl CopyOnWrite { } } -impl Spanned<&mut CopyOnWrite> { +impl Spanned<&mut CopyOnWrite> { /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -676,9 +676,9 @@ impl Deref for CopyOnWrite { } } -impl CopyOnWrite { +impl CopyOnWrite { /// Converts to owned, cloning if necessary - pub(crate) fn into_owned_infallible(self) -> OwnedValue { + pub(crate) fn clone_to_owned_infallible(self) -> OwnedValue { match self.inner { CopyOnWriteInner::Owned(owned) => owned, CopyOnWriteInner::SharedWithInfallibleCloning(shared) => shared.infallible_clone(), @@ -687,7 +687,10 @@ impl CopyOnWrite { } /// Converts to owned, using transparent clone for shared values where cloning was not requested - pub(crate) fn into_owned_transparently(self, span: SpanRange) -> ExecutionResult { + pub(crate) fn clone_to_owned_transparently( + self, + span: SpanRange, + ) -> ExecutionResult { match self.inner { CopyOnWriteInner::Owned(owned) => Ok(owned), CopyOnWriteInner::SharedWithInfallibleCloning(shared) => Ok(shared.infallible_clone()), @@ -708,4 +711,4 @@ impl CopyOnWrite { } } -pub(crate) type CopyOnWriteValue = CopyOnWrite; +pub(crate) type CopyOnWriteValue = CopyOnWrite; diff --git a/src/interpretation/interpreter.rs b/src/interpretation/interpreter.rs index 452a9d9a..8416046a 100644 --- a/src/interpretation/interpreter.rs +++ b/src/interpretation/interpreter.rs @@ -163,7 +163,7 @@ impl Interpreter { } } - pub(crate) fn define_variable(&mut self, definition_id: VariableDefinitionId, value: Value) { + pub(crate) fn define_variable(&mut self, definition_id: VariableDefinitionId, value: AnyValue) { let definition = self.scope_definitions.definitions.get(definition_id); let scope_data = self.scope_mut(definition.scope); scope_data.define_variable(definition_id, value) @@ -400,7 +400,7 @@ struct RuntimeScope { } impl RuntimeScope { - fn define_variable(&mut self, definition_id: VariableDefinitionId, value: Value) { + fn define_variable(&mut self, definition_id: VariableDefinitionId, value: AnyValue) { self.variables .get_mut(&definition_id) .expect("Variable data not found in scope") diff --git a/src/interpretation/output_stream.rs b/src/interpretation/output_stream.rs index 0072a0e9..e60b862e 100644 --- a/src/interpretation/output_stream.rs +++ b/src/interpretation/output_stream.rs @@ -120,10 +120,10 @@ impl OutputStream { self.token_length == 0 } - pub(crate) fn coerce_into_value(self) -> Value { + pub(crate) fn coerce_into_value(self) -> AnyValue { let parse_result = self.clone().parse_as::(); match parse_result { - Ok(syn_lit) => Value::for_syn_lit(syn_lit).into_inner(), + Ok(syn_lit) => AnyValue::for_syn_lit(syn_lit).into_inner(), // Keep as stream otherwise Err(_) => self.into_value(), } diff --git a/src/interpretation/refs.rs b/src/interpretation/refs.rs index e9f61f46..776a2a3a 100644 --- a/src/interpretation/refs.rs +++ b/src/interpretation/refs.rs @@ -78,7 +78,7 @@ impl ToSpannedRef<'static> for Shared { enum AnyRefInner<'a, T: 'static + ?Sized> { Direct(&'a T), - Encapsulated(SharedSubRcRefCell), + Encapsulated(SharedSubRcRefCell), } impl<'a, T: 'static + ?Sized> Deref for AnyRef<'a, T> { @@ -161,7 +161,7 @@ impl<'a, T: ?Sized> From> for AnyMut<'a, T> { enum AnyMutInner<'a, T: 'static + ?Sized> { Direct(&'a mut T), - Encapsulated(MutableSubRcRefCell), + Encapsulated(MutableSubRcRefCell), } impl<'a, T: 'static + ?Sized> Deref for AnyMut<'a, T> { diff --git a/src/interpretation/variable.rs b/src/interpretation/variable.rs index 55e7f5fa..40a94104 100644 --- a/src/interpretation/variable.rs +++ b/src/interpretation/variable.rs @@ -125,7 +125,7 @@ impl VariableReference { grouping: Grouping, ) -> ExecutionResult<()> { let value = self.resolve_shared(interpreter)?; - value.output_to( + value.as_ref_value().output_to( grouping, &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), ) @@ -185,7 +185,7 @@ impl HandleDestructure for VariablePattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { self.definition.define(interpreter, value); Ok(()) diff --git a/src/lib.rs b/src/lib.rs index aa43efe6..b3362b4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,7 +461,7 @@ fn preinterpret_run_internal(input: TokenStream) -> SynResult { let entry_span = Span::call_site().span_range(); let returned_stream = content .evaluate_spanned(&mut interpreter, entry_span, RequestedOwnership::owned()) - .and_then(|x| x.expect_owned().into_stream()) + .and_then(|x| x.expect_owned().map(|owned| owned.0).into_stream()) .convert_to_final_result()?; let mut output_stream = interpreter.complete(); @@ -584,7 +584,7 @@ mod benchmarking { let entry_span = Span::call_site().span_range(); let returned_stream = parsed .evaluate_spanned(&mut interpreter, entry_span, RequestedOwnership::owned()) - .and_then(|x| x.expect_owned().into_stream()) + .and_then(|x| x.expect_owned().map(|owned| owned.0).into_stream()) .convert_to_final_result()?; let mut output_stream = interpreter.complete(); diff --git a/src/misc/errors.rs b/src/misc/errors.rs index e44afd76..f5a05057 100644 --- a/src/misc/errors.rs +++ b/src/misc/errors.rs @@ -320,7 +320,7 @@ pub(crate) struct BreakInterrupt { } impl BreakInterrupt { - pub(crate) fn into_value( + pub(crate) fn into_requested_value( self, span_range: SpanRange, ownership: RequestedOwnership, diff --git a/src/misc/field_inputs.rs b/src/misc/field_inputs.rs index 987d66f7..fbeea261 100644 --- a/src/misc/field_inputs.rs +++ b/src/misc/field_inputs.rs @@ -67,8 +67,8 @@ macro_rules! define_typed_object { type ValueType = ObjectType; } - impl ResolvableOwned for $model { - fn resolve_from_value(value: Value, context: ResolutionContext) -> ExecutionResult { + impl ResolvableOwned for $model { + fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { Self::from_object_value(ObjectValue::resolve_spanned_owned_from_value(value, context)?) } } diff --git a/src/misc/iterators.rs b/src/misc/iterators.rs index 1722d163..cb2e3aed 100644 --- a/src/misc/iterators.rs +++ b/src/misc/iterators.rs @@ -56,10 +56,8 @@ impl ZipIterators { k, v.key_span, v.value - .into_owned() .spanned(span_range) - .resolve_any_iterator("Each zip input")? - .into_inner(), + .resolve_any_iterator("Each zip input")?, )) }) .collect::, _>>()?; @@ -75,12 +73,7 @@ impl ZipIterators { ) -> ExecutionResult { let vec = iterator .take(101) - .map(|x| { - x.into_owned() - .spanned(span_range) - .resolve_any_iterator("Each zip input") - .map(|x| x.into_inner()) - }) + .map(|x| x.spanned(span_range).resolve_any_iterator("Each zip input")) .collect::, _>>()?; if vec.len() == 101 { return span_range.value_err("A maximum of 100 iterators are allowed"); @@ -160,7 +153,7 @@ impl ZipIterators { count: usize, interpreter: &mut Interpreter, error_span_range: SpanRange, - output: &mut Vec, + output: &mut Vec, ) -> ExecutionResult<()> { let mut counter = interpreter.start_iteration_counter(&error_span_range); @@ -200,13 +193,13 @@ impl ZipIterators { define_optional_object! { pub(crate) struct IntersperseSettings { add_trailing: bool = false => ("false", "Whether to add the separator after the last item (default: false)"), - final_separator: Value => ("%[or]", "Define a different final separator (default: same as normal separator)"), + final_separator: AnyValue => ("%[or]", "Define a different final separator (default: same as normal separator)"), } } pub(crate) fn run_intersperse( items: IterableValue, - separator: Value, + separator: AnyValue, settings: IntersperseSettings, ) -> ExecutionResult { let mut output = Vec::new(); @@ -248,8 +241,8 @@ pub(crate) fn run_intersperse( } struct SeparatorAppender { - separator: Value, - final_separator: Option, + separator: AnyValue, + final_separator: Option, add_trailing: bool, } @@ -257,7 +250,7 @@ impl SeparatorAppender { fn add_separator( &mut self, remaining: RemainingItemCount, - output: &mut Vec, + output: &mut Vec, ) -> ExecutionResult<()> { match self.separator(remaining) { TrailingSeparator::Normal => output.push(self.separator.clone()), diff --git a/src/misc/mod.rs b/src/misc/mod.rs index 4e685bd9..4e053fe2 100644 --- a/src/misc/mod.rs +++ b/src/misc/mod.rs @@ -39,7 +39,7 @@ pub(crate) fn print_if_slow( pub(crate) enum Never {} impl IntoValue for Never { - fn into_value(self) -> Value { + fn into_value(self) -> AnyValue { match self {} } } From 15efe69d98f7502691a0182af2bbc23f1e1db22e Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 8 Jan 2026 02:31:01 +0100 Subject: [PATCH 32/58] refactor: Remove `Owned` as its own wrapper --- plans/TODO.md | 4 +- src/expressions/concepts/content.rs | 4 +- src/expressions/concepts/forms/owned.rs | 15 +- src/expressions/control_flow.rs | 2 +- .../evaluation/assignment_frames.rs | 10 +- src/expressions/evaluation/evaluator.rs | 24 ++-- src/expressions/evaluation/value_frames.rs | 50 +++---- src/expressions/expression.rs | 2 +- src/expressions/expression_block.rs | 6 +- src/expressions/expression_parsing.rs | 4 +- src/expressions/operations.rs | 22 +-- src/expressions/patterns.rs | 6 +- src/expressions/statements.rs | 4 +- src/expressions/type_resolution/arguments.rs | 51 +------ src/expressions/type_resolution/outputs.rs | 16 +-- src/expressions/type_resolution/type_kinds.rs | 2 +- src/expressions/values/any_value.rs | 42 +++--- src/expressions/values/array.rs | 19 ++- src/expressions/values/float_subtypes.rs | 16 +-- src/expressions/values/integer.rs | 5 +- src/expressions/values/integer_subtypes.rs | 9 +- src/expressions/values/integer_untyped.rs | 3 +- src/expressions/values/iterator.rs | 20 +-- src/expressions/values/object.rs | 8 +- src/expressions/values/parser.rs | 36 +++-- src/expressions/values/range.rs | 32 ++--- src/expressions/values/stream.rs | 33 +++-- src/expressions/values/string.rs | 6 +- src/interpretation/bindings.rs | 129 ++++-------------- src/interpretation/output_stream.rs | 4 +- src/interpretation/variable.rs | 4 +- src/lib.rs | 2 +- src/misc/errors.rs | 6 +- src/misc/field_inputs.rs | 9 +- src/misc/iterators.rs | 10 +- src/misc/mod.rs | 4 +- 36 files changed, 248 insertions(+), 371 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 5836344f..8c232e32 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -243,8 +243,8 @@ First, read the @./2025-11-vision.md - [x] `type AnyValueRef = QqqRef` - [x] `type AnyValueMut = QqqMut` - [x] ... and move methods - - [ ] Remove `OwnedValue` - - [ ] Get rid of `Owned` + - [x] Remove `OwnedValue` + - [x] Get rid of `Owned` - [ ] Stage 2 of the form migration: - [ ] Improve mappers: - [ ] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 6b81c924..af983c39 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -121,12 +121,12 @@ where } // TODO[concepts]: Remove eventually, along with IntoValue impl -impl> IntoValue for X +impl> IntoAnyValue for X where X::Type: UpcastTo, BeOwned: IsFormOf, { - fn into_value(self) -> AnyValue { + fn into_any_value(self) -> AnyValue { self.into_any() } } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 7485e42f..340c63f6 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -1,12 +1,11 @@ use super::*; -pub(crate) type QqqOwned = Actual<'static, T, BeOwned>; - -pub(crate) type QqqOwnedValue = Owned; +/// Just [`T`]! This exists simply to be a name for symmetry with e.g. Shared or Mutable. +pub(crate) type Owned = T; /// Represents floating owned values. /// -/// If you need span information, wrap with `Spanned`. For example, with `x.y[4]`, this would capture both: +/// If you need span information, wrap with `Spanned`. For example, with `x.y[4]`, this would capture both: /// * The output owned value /// * The lexical span of the tokens `x.y[4]` #[derive(Copy, Clone)] @@ -47,7 +46,7 @@ impl MapFromArgument for BeOwned { fn from_argument_value( value: ArgumentValue, ) -> ExecutionResult> { - Ok(value.expect_owned().0) + Ok(value.expect_owned()) } } @@ -84,7 +83,7 @@ mod test { #[test] fn can_resolve_owned() { - let owned_value: QqqOwned = 42u64; + let owned_value: Owned<_> = 42u64; let resolved = owned_value .spanned(Span::call_site().span_range()) .downcast_resolve::("My value") @@ -94,14 +93,14 @@ mod test { #[test] fn can_as_ref_owned() { - let owned_value: QqqOwned = 42u64; + let owned_value = 42u64; let as_ref: QqqRef = owned_value.as_ref_value(); assert_eq!(*as_ref, 42u64); } #[test] fn can_as_mut_owned() { - let mut owned_value: QqqOwned = 42u64; + let mut owned_value = 42u64; *owned_value.as_mut_value() = 41u64; assert_eq!(owned_value, 41u64); } diff --git a/src/expressions/control_flow.rs b/src/expressions/control_flow.rs index 6c77b8fc..3e1dc39b 100644 --- a/src/expressions/control_flow.rs +++ b/src/expressions/control_flow.rs @@ -126,7 +126,7 @@ impl Evaluate for IfExpression { } requested_ownership - .map_from_owned(Spanned(().into_owned_value(), self.span_range())) + .map_from_owned(Spanned(().into_any_value(), self.span_range())) .map(|spanned| spanned.0) } } diff --git a/src/expressions/evaluation/assignment_frames.rs b/src/expressions/evaluation/assignment_frames.rs index d760685f..933201c8 100644 --- a/src/expressions/evaluation/assignment_frames.rs +++ b/src/expressions/evaluation/assignment_frames.rs @@ -116,8 +116,8 @@ impl ArrayBasedAssigner { value: AnyValue, ) -> ExecutionResult { let span_range = assignee_span.span_range(); - let array: ArrayValue = Spanned(value.into_owned(), span_range) - .resolve_as("The value destructured as an array")?; + let array: ArrayValue = + Spanned(value, span_range).resolve_as("The value destructured as an array")?; let mut has_seen_dot_dot = false; let mut prefix_assignees = Vec::new(); let mut suffix_assignees = Vec::new(); @@ -243,8 +243,8 @@ impl ObjectBasedAssigner { value: AnyValue, ) -> ExecutionResult { let span_range = assignee_span.span_range(); - let object: ObjectValue = Spanned(value.into_owned(), span_range) - .resolve_as("The value destructured as an object")?; + let object: ObjectValue = + Spanned(value, span_range).resolve_as("The value destructured as an object")?; Ok(Self { span_range, @@ -300,7 +300,7 @@ impl ObjectBasedAssigner { .entries .remove(&key) .map(|entry| entry.value) - .unwrap_or_else(|| ().into_value()); + .unwrap_or_else(|| ().into_any_value()); self.already_used_keys.insert(key); Ok(value) } diff --git a/src/expressions/evaluation/evaluator.rs b/src/expressions/evaluation/evaluator.rs index 79d33717..75422457 100644 --- a/src/expressions/evaluation/evaluator.rs +++ b/src/expressions/evaluation/evaluator.rs @@ -146,9 +146,9 @@ impl From for NextAction { pub(crate) enum RequestedValue { // RequestedOwnership::Concrete(_) // ------------------------------- - Owned(OwnedValue), - Shared(SharedValue), - Mutable(MutableValue), + Owned(Owned), + Shared(Shared), + Mutable(Mutable), CopyOnWrite(CopyOnWriteValue), Assignee(AssigneeValue), @@ -162,21 +162,21 @@ pub(crate) enum RequestedValue { } impl RequestedValue { - pub(crate) fn expect_owned(self) -> OwnedValue { + pub(crate) fn expect_owned(self) -> Owned { match self { RequestedValue::Owned(value) => value, _ => panic!("expect_owned() called on non-owned RequestedValue"), } } - pub(crate) fn expect_shared(self) -> SharedValue { + pub(crate) fn expect_shared(self) -> Shared { match self { RequestedValue::Shared(shared) => shared, _ => panic!("expect_shared() called on non-shared RequestedValue"), } } - pub(super) fn expect_assignee(self) -> AssigneeValue { + pub(super) fn expect_assignee(self) -> Assignee { match self { RequestedValue::Assignee(assignee) => assignee, _ => panic!("expect_assignee() called on non-assignee RequestedValue"), @@ -214,9 +214,9 @@ impl RequestedValue { pub(crate) fn expect_any_value_and_map( self, - map_shared: impl FnOnce(SharedValue) -> ExecutionResult, - map_mutable: impl FnOnce(MutableValue) -> ExecutionResult, - map_owned: impl FnOnce(OwnedValue) -> ExecutionResult, + map_shared: impl FnOnce(Shared) -> ExecutionResult>, + map_mutable: impl FnOnce(Mutable) -> ExecutionResult>, + map_owned: impl FnOnce(Owned) -> ExecutionResult>, ) -> ExecutionResult { Ok(match self { RequestedValue::LateBound(late_bound) => { @@ -241,17 +241,17 @@ impl RequestedValue { #[allow(unused)] impl Spanned { #[inline] - pub(crate) fn expect_owned(self) -> Spanned { + pub(crate) fn expect_owned(self) -> Spanned> { self.map(|v| v.expect_owned()) } #[inline] - pub(crate) fn expect_shared(self) -> Spanned { + pub(crate) fn expect_shared(self) -> Spanned> { self.map(|v| v.expect_shared()) } #[inline] - pub(crate) fn expect_assignee(self) -> Spanned { + pub(crate) fn expect_assignee(self) -> Spanned> { self.map(|v| v.expect_assignee()) } diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index dbee547d..84b96d29 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -6,15 +6,15 @@ use super::*; /// It is typically paired with an [`ArgumentOwnership`] (maybe wrapped in a [`RequestedOwnership`]) /// which indicates what ownership type to resolve to. pub(crate) enum ArgumentValue { - Owned(OwnedValue), + Owned(Owned), CopyOnWrite(CopyOnWriteValue), - Mutable(MutableValue), - Assignee(AssigneeValue), - Shared(SharedValue), + Mutable(Mutable), + Assignee(Assignee), + Shared(Shared), } impl ArgumentValue { - pub(crate) fn expect_owned(self) -> OwnedValue { + pub(crate) fn expect_owned(self) -> Owned { match self { ArgumentValue::Owned(value) => value, _ => panic!("expect_owned() called on a non-owned ArgumentValue"), @@ -28,21 +28,21 @@ impl ArgumentValue { } } - pub(crate) fn expect_mutable(self) -> MutableValue { + pub(crate) fn expect_mutable(self) -> Mutable { match self { ArgumentValue::Mutable(value) => value, _ => panic!("expect_mutable() called on a non-mutable ArgumentValue"), } } - pub(crate) fn expect_assignee(self) -> AssigneeValue { + pub(crate) fn expect_assignee(self) -> Assignee { match self { ArgumentValue::Assignee(value) => value, _ => panic!("expect_assignee() called on a non-assignee ArgumentValue"), } } - pub(crate) fn expect_shared(self) -> SharedValue { + pub(crate) fn expect_shared(self) -> Shared { match self { ArgumentValue::Shared(value) => value, _ => panic!("expect_shared() called on a non-shared ArgumentValue"), @@ -52,22 +52,22 @@ impl ArgumentValue { impl Spanned { #[inline] - pub(crate) fn expect_owned(self) -> Spanned { + pub(crate) fn expect_owned(self) -> Spanned> { self.map(|value| value.expect_owned()) } #[inline] - pub(crate) fn expect_mutable(self) -> Spanned { + pub(crate) fn expect_mutable(self) -> Spanned> { self.map(|value| value.expect_mutable()) } #[inline] - pub(crate) fn expect_assignee(self) -> Spanned { + pub(crate) fn expect_assignee(self) -> Spanned> { self.map(|value| value.expect_assignee()) } #[inline] - pub(crate) fn expect_shared(self) -> Spanned { + pub(crate) fn expect_shared(self) -> Spanned> { self.map(|value| value.expect_shared()) } } @@ -169,7 +169,7 @@ impl RequestedOwnership { } pub(crate) fn map_none(self, span: SpanRange) -> ExecutionResult> { - self.map_from_owned(Spanned(().into_owned_value(), span)) + self.map_from_owned(Spanned(().into_any_value(), span)) } pub(crate) fn map_from_late_bound( @@ -240,7 +240,7 @@ impl RequestedOwnership { pub(crate) fn map_from_owned( &self, - Spanned(value, span): Spanned, + Spanned(value, span): Spanned, ) -> ExecutionResult> { Ok(Spanned( match self { @@ -277,7 +277,7 @@ impl RequestedOwnership { pub(crate) fn map_from_mutable( &self, - Spanned(mutable, span): Spanned, + Spanned(mutable, span): Spanned>, ) -> ExecutionResult> { Ok(Spanned( match self { @@ -466,7 +466,7 @@ impl ArgumentOwnership { pub(crate) fn map_from_mutable( &self, - spanned_mutable: Spanned, + spanned_mutable: Spanned>, ) -> ExecutionResult { self.map_from_mutable_inner(spanned_mutable, false) } @@ -480,7 +480,7 @@ impl ArgumentOwnership { fn map_from_mutable_inner( &self, - Spanned(mutable, span): Spanned, + Spanned(mutable, span): Spanned>, is_late_bound: bool, ) -> ExecutionResult { match self { @@ -506,14 +506,14 @@ impl ArgumentOwnership { pub(crate) fn map_from_owned( &self, - owned: Spanned, + owned: Spanned, ) -> ExecutionResult { self.map_from_owned_with_is_last_use(owned, false) } fn map_from_owned_with_is_last_use( &self, - Spanned(owned, span): Spanned, + Spanned(owned, span): Spanned, is_from_last_use: bool, ) -> ExecutionResult { match self { @@ -655,7 +655,7 @@ impl EvaluationFrame for ArrayBuilder { Spanned(value, _span): Spanned, ) -> ExecutionResult { let value = value.expect_owned(); - self.evaluated_items.push(value.into_inner()); + self.evaluated_items.push(value); self.next(context) } } @@ -750,7 +750,7 @@ impl EvaluationFrame for Box { context.request_owned(self, value_node) } Some(PendingEntryPath::OnValueBranch { key, key_span }) => { - let value = value.expect_owned().into_inner(); + let value = value.expect_owned(); let entry = ObjectEntry { key_span, value }; self.evaluated_entries.insert(key, entry); self.next(context)? @@ -965,7 +965,7 @@ impl EvaluationFrame for ValuePropertyAccessBuilder { mutable .try_map(|value| value.as_mut_value().property_mut(&self.access, auto_create)) }, - |owned| owned.try_map(|value| value.into_property(&self.access)), + |owned| owned.into_property(&self.access), )?; // The result span covers source through property let result_span = SpanRange::new_between(source_span, self.access.span_range()); @@ -1044,7 +1044,7 @@ impl EvaluationFrame for ValueIndexAccessBuilder { .index_mut(self.access, index, auto_create) }) }, - |owned| owned.try_map(|value| value.into_indexed(self.access, index)), + |owned| owned.into_indexed(self.access, index), )?; let result_span = SpanRange::new_between(source_span, self.access.span_range()); context.return_not_necessarily_matching_requested(Spanned(result, result_span))? @@ -1113,7 +1113,7 @@ impl EvaluationFrame for RangeBuilder { Spanned(value, _span): Spanned, ) -> ExecutionResult { // TODO[range-refactor]: Change to not always clone the value - let value = value.expect_owned().into_inner(); + let value = value.expect_owned(); Ok(match (self.state, self.range_limits) { (RangePath::OnLeftBranch { right: Some(right) }, _) => { self.state = RangePath::OnRightBranch { left: Some(value) }; @@ -1203,7 +1203,7 @@ impl EvaluationFrame for AssignmentBuilder { ) -> ExecutionResult { Ok(match self.state { AssignmentPath::OnValueBranch { assignee } => { - let value = value.expect_owned().into_inner(); + let value = value.expect_owned(); self.state = AssignmentPath::OnAwaitingAssignment; context.request_assignment(self, assignee, value) } diff --git a/src/expressions/expression.rs b/src/expressions/expression.rs index 23da418c..3912aa90 100644 --- a/src/expressions/expression.rs +++ b/src/expressions/expression.rs @@ -41,7 +41,7 @@ impl Expression { pub(crate) fn evaluate_owned( &self, interpreter: &mut Interpreter, - ) -> ExecutionResult> { + ) -> ExecutionResult> { Ok(self .evaluate(interpreter, RequestedOwnership::owned())? .expect_owned()) diff --git a/src/expressions/expression_block.rs b/src/expressions/expression_block.rs index 306b7e04..cacdfcca 100644 --- a/src/expressions/expression_block.rs +++ b/src/expressions/expression_block.rs @@ -225,7 +225,7 @@ impl ScopedBlock { pub(crate) fn evaluate_owned( &self, interpreter: &mut Interpreter, - ) -> ExecutionResult> { + ) -> ExecutionResult> { let span_range = self.span().span_range(); self.evaluate(interpreter, RequestedOwnership::owned()) .map(|value| Spanned(value.expect_owned(), span_range)) @@ -271,7 +271,7 @@ impl UnscopedBlock { pub(crate) fn evaluate_owned( &self, interpreter: &mut Interpreter, - ) -> ExecutionResult> { + ) -> ExecutionResult> { let span_range = self.span().span_range(); self.evaluate(interpreter, RequestedOwnership::owned()) .map(|value| Spanned(value.expect_owned(), span_range)) @@ -332,6 +332,6 @@ impl ExpressionBlockContent { statement.evaluate_as_statement(interpreter)?; } } - ownership.map_from_owned(Spanned(().into_owned_value(), output_span)) + ownership.map_from_owned(Spanned(().into_any_value(), output_span)) } } diff --git a/src/expressions/expression_parsing.rs b/src/expressions/expression_parsing.rs index 77f2078b..2fc27fdf 100644 --- a/src/expressions/expression_parsing.rs +++ b/src/expressions/expression_parsing.rs @@ -121,7 +121,7 @@ impl<'a> ExpressionParser<'a> { "true" | "false" => { let bool = input.parse::()?; UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(bool.value.into_owned_value()), + SharedValue::new_from_owned(bool.value.into_any_value()), bool.span.span_range(), ))) } @@ -134,7 +134,7 @@ impl<'a> ExpressionParser<'a> { "None" => { let none_ident = input.parse_any_ident()?; // consume the "None" token UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(().into_owned_value()), + SharedValue::new_from_owned(().into_any_value()), none_ident.span().span_range(), ))) } diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index 4b143219..271198be 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -82,11 +82,11 @@ impl UnaryOperation { operand_span_range } - pub(super) fn evaluate( + pub(super) fn evaluate( &self, - Spanned(input, input_span): Spanned>, + Spanned(input, input_span): Spanned, ) -> ExecutionResult> { - let input = input.into_owned_value(); + let input = input.into_any_value(); let method = input .kind() .feature_resolver() @@ -278,12 +278,12 @@ impl BinaryOperation { pub(super) fn lazy_evaluate( &self, left: Spanned<&AnyValue>, - ) -> ExecutionResult> { + ) -> ExecutionResult> { match self { BinaryOperation::LogicalAnd { .. } => { let bool: Spanned<&bool> = left.resolve_as("The left operand to &&")?; if !**bool { - Ok(Some((*bool).into_owned_value())) + Ok(Some((*bool).into_any_value())) } else { Ok(None) } @@ -291,7 +291,7 @@ impl BinaryOperation { BinaryOperation::LogicalOr { .. } => { let bool: Spanned<&bool> = left.resolve_as("The left operand to ||")?; if **bool { - Ok(Some((*bool).into_owned_value())) + Ok(Some((*bool).into_any_value())) } else { Ok(None) } @@ -301,13 +301,13 @@ impl BinaryOperation { } #[allow(unused)] - pub(crate) fn evaluate( + pub(crate) fn evaluate( &self, - Spanned(left, left_span): Spanned>, - Spanned(right, right_span): Spanned>, + Spanned(left, left_span): Spanned, + Spanned(right, right_span): Spanned, ) -> ExecutionResult> { - let left = left.into_owned_value(); - let right = right.into_owned_value(); + let left = left.into_any_value(); + let right = right.into_any_value(); match left .kind() .feature_resolver() diff --git a/src/expressions/patterns.rs b/src/expressions/patterns.rs index 27d02ed5..1f07033c 100644 --- a/src/expressions/patterns.rs +++ b/src/expressions/patterns.rs @@ -113,7 +113,7 @@ impl HandleDestructure for ArrayPattern { interpreter: &mut Interpreter, value: AnyValue, ) -> ExecutionResult<()> { - let array: ArrayValue = Spanned(value.into_owned(), self.brackets.span_range()) + let array: ArrayValue = Spanned(value, self.brackets.span_range()) .resolve_as("The value destructured with an array pattern")?; let mut has_seen_dot_dot = false; let mut prefix_assignees = Vec::new(); @@ -228,7 +228,7 @@ impl HandleDestructure for ObjectPattern { interpreter: &mut Interpreter, value: AnyValue, ) -> ExecutionResult<()> { - let object: ObjectValue = Spanned(value.into_owned(), self.braces.span_range()) + let object: ObjectValue = Spanned(value, self.braces.span_range()) .resolve_as("The value destructured with an object pattern")?; let mut value_map = object.entries; let mut already_used_keys = HashSet::with_capacity(self.entries.len()); @@ -253,7 +253,7 @@ impl HandleDestructure for ObjectPattern { let value = value_map .remove(&key) .map(|entry| entry.value) - .unwrap_or_else(|| ().into_value()); + .unwrap_or_else(|| ().into_any_value()); already_used_keys.insert(key); pattern.handle_destructure(interpreter, value)?; } diff --git a/src/expressions/statements.rs b/src/expressions/statements.rs index adc76fd5..e0d4fca3 100644 --- a/src/expressions/statements.rs +++ b/src/expressions/statements.rs @@ -144,8 +144,8 @@ impl LetStatement { assignment, } = self; let value = match assignment { - Some(assignment) => assignment.expression.evaluate_owned(interpreter)?.0 .0, - None => ().into_value(), + Some(assignment) => assignment.expression.evaluate_owned(interpreter)?.0, + None => ().into_any_value(), }; pattern.handle_destructure(interpreter, value)?; Ok(()) diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index 97c857a4..b8b3b5eb 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -111,24 +111,12 @@ where } } -impl + ResolvableArgumentTarget> IsArgument for Owned { - type ValueType = T::ValueType; - const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - - fn from_argument(argument: Spanned) -> ExecutionResult { - T::resolve_owned(argument.expect_owned(), "This argument") - } -} - impl + ResolvableArgumentTarget> IsArgument for T { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; fn from_argument(argument: Spanned) -> ExecutionResult { - T::resolve_value( - argument.expect_owned().map(|owned| owned.0), - "This argument", - ) + T::resolve_value(argument.expect_owned(), "This argument") } } @@ -144,7 +132,7 @@ where value.expect_copy_on_write().map( |v| T::resolve_shared(v.spanned(span), "This argument"), |v| { - >::resolve_owned( + >::resolve_value( v.spanned(span), "This argument", ) @@ -168,18 +156,12 @@ pub(crate) trait ResolveAs { fn resolve_as(self, resolution_target: &str) -> ExecutionResult; } -impl, V> ResolveAs for Spanned> { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult { - T::resolve_value(self.map(|owned| owned.0), resolution_target) - } -} - // Sadly this can't be changed Value => V because of spurious issues with // https://github.com/rust-lang/rust/issues/48869 // Instead, we could introduce a different trait ResolveAs2 if needed. -impl> ResolveAs> for Spanned> { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { - T::resolve_owned(self, resolution_target) +impl> ResolveAs for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult { + T::resolve_value(self, resolution_target) } } @@ -232,19 +214,12 @@ pub(crate) trait ResolvableArgumentTarget { pub(crate) trait ResolvableOwned: Sized { fn resolve_from_value(value: T, context: ResolutionContext) -> ExecutionResult; - fn resolve_spanned_owned_from_value( + fn resolve_spanned_from_value( value: T, context: ResolutionContext, ) -> ExecutionResult>> { let span_range = context.span_range; - Self::resolve_from_value(value, context).map(|v| Owned(v).spanned(*span_range)) - } - - fn resolve_owned_from_value( - value: T, - context: ResolutionContext, - ) -> ExecutionResult> { - Self::resolve_from_value(value, context).map(Owned::new) + Self::resolve_from_value(value, context).map(|x| x.spanned(*span_range)) } /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" @@ -258,18 +233,6 @@ pub(crate) trait ResolvableOwned: Sized { }; Self::resolve_from_value(value, context) } - - /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" - fn resolve_owned( - Spanned(value, span): Spanned>, - resolution_target: &str, - ) -> ExecutionResult> { - let context = ResolutionContext { - span_range: &span, - resolution_target, - }; - Self::resolve_from_value(value.into_inner(), context).map(Owned::new) - } } pub(crate) trait ResolvableShared { diff --git a/src/expressions/type_resolution/outputs.rs b/src/expressions/type_resolution/outputs.rs index f79c7677..3bc93c96 100644 --- a/src/expressions/type_resolution/outputs.rs +++ b/src/expressions/type_resolution/outputs.rs @@ -5,10 +5,10 @@ use super::*; /// /// See also [`RequestedValue`] for values that have been fully evaluated. pub(crate) enum ReturnedValue { - Owned(OwnedValue), + Owned(Owned), CopyOnWrite(CopyOnWriteValue), - Mutable(MutableValue), - Shared(SharedValue), + Mutable(Mutable), + Shared(Shared), } // TODO: Find some way to selectively enable only on MSRV (e.g. following the build.rs feature flag pattern) @@ -38,15 +38,9 @@ impl IsReturnable for Mutable { } } -impl IsReturnable for T { +impl IsReturnable for T { fn to_returned_value(self) -> ExecutionResult { - Ok(ReturnedValue::Owned(Owned(self.into_value()))) - } -} - -impl IsReturnable for Owned { - fn to_returned_value(self) -> ExecutionResult { - Ok(ReturnedValue::Owned(self.map(|f| f.into_value()))) + Ok(ReturnedValue::Owned(self.into_any_value())) } } diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs index 15a545bc..7c27e743 100644 --- a/src/expressions/type_resolution/type_kinds.rs +++ b/src/expressions/type_resolution/type_kinds.rs @@ -241,7 +241,7 @@ impl TypeProperty { let resolved_property = resolver.resolve_type_property(&self.property.to_string()); match resolved_property { Some(value) => ownership.map_from_shared(Spanned( - SharedValue::new_from_owned(value.into_owned_value()), + SharedValue::new_from_owned(value.into_any_value()), self.span_range(), )), None => self.type_err(format!( diff --git a/src/expressions/values/any_value.rs b/src/expressions/values/any_value.rs index afdab3d8..b9b5d7f9 100644 --- a/src/expressions/values/any_value.rs +++ b/src/expressions/values/any_value.rs @@ -34,11 +34,11 @@ define_type_features! { impl AnyType, pub(crate) mod value_interface { pub(crate) mod methods { - fn clone(this: CopyOnWriteValue) -> OwnedValue { + fn clone(this: CopyOnWriteValue) -> AnyValue { this.clone_to_owned_infallible() } - fn as_mut(Spanned(this, span): Spanned) -> ExecutionResult { + fn as_mut(Spanned(this, span): Spanned) -> ExecutionResult> { Ok(match this { ArgumentValue::Owned(owned) => Mutable::new_from_owned(owned), ArgumentValue::CopyOnWrite(copy_on_write) => ArgumentOwnership::Mutable @@ -78,14 +78,14 @@ define_type_features! { fn to_stream(Spanned(input, span_range): Spanned) -> ExecutionResult { input.map_into( |shared| shared.as_ref_value().output_to_new_stream(Grouping::Flattened, span_range), - |owned| owned.0.into_stream(Grouping::Flattened, span_range), + |owned| owned.into_stream(Grouping::Flattened, span_range), ) } fn to_group(Spanned(input, span_range): Spanned) -> ExecutionResult { input.map_into( |shared| shared.as_ref_value().output_to_new_stream(Grouping::Grouped, span_range), - |owned| owned.0.into_stream(Grouping::Grouped, span_range), + |owned| owned.into_stream(Grouping::Grouped, span_range), ) } @@ -193,14 +193,8 @@ define_type_features! { } } -pub(crate) trait IntoValue: Sized { - fn into_value(self) -> AnyValue; - fn into_owned(self) -> Owned { - Owned::new(self) - } - fn into_owned_value(self) -> OwnedValue { - Owned(self.into_value()) - } +pub(crate) trait IntoAnyValue: Sized { + fn into_any_value(self) -> AnyValue; } impl<'a, F: IsHierarchicalForm> AnyValueContent<'a, F> { @@ -210,7 +204,7 @@ impl<'a, F: IsHierarchicalForm> AnyValueContent<'a, F> { } impl AnyValue { - pub(crate) fn for_literal(literal: Literal) -> OwnedValue { + pub(crate) fn for_literal(literal: Literal) -> AnyValue { // The unwrap should be safe because all Literal should be parsable // as syn::Lit; falling back to syn::Lit::Verbatim if necessary. Self::for_syn_lit( @@ -221,25 +215,25 @@ impl AnyValue { ) } - pub(crate) fn for_syn_lit(lit: syn::Lit) -> OwnedValue { + pub(crate) fn for_syn_lit(lit: syn::Lit) -> AnyValue { // https://docs.rs/syn/latest/syn/enum.Lit.html let matched = match &lit { Lit::Int(lit) => match IntegerValue::for_litint(lit) { - Ok(int) => Some(int.into_owned_value()), + Ok(int) => Some(int.into_any_value()), Err(_) => None, }, Lit::Float(lit) => match FloatValue::for_litfloat(lit) { - Ok(float) => Some(float.into_owned_value()), + Ok(float) => Some(float.into_any_value()), Err(_) => None, }, - Lit::Bool(lit) => Some(lit.value.into_owned_value()), - Lit::Str(lit) => Some(lit.value().into_owned_value()), - Lit::Char(lit) => Some(lit.value().into_owned_value()), + Lit::Bool(lit) => Some(lit.value.into_any_value()), + Lit::Str(lit) => Some(lit.value().into_any_value()), + Lit::Char(lit) => Some(lit.value().into_any_value()), _ => None, }; match matched { Some(value) => value, - None => UnsupportedLiteral(lit).into_owned_value(), + None => UnsupportedLiteral(lit).into_any_value(), } } @@ -586,6 +580,14 @@ impl HasSpanRange for ToStreamContext<'_> { } impl Spanned { + pub(crate) fn into_statement_result(self) -> ExecutionResult<()> { + let Spanned(value, span_range) = self; + match value { + AnyValueContent::None(_) => Ok(()), + _ => span_range.control_flow_err("A non-returning statement must not return a value. If you wish to explicitly discard the expression's result, use `let _ = ...;`. Alternatively, If you wish to output the value into the parent token stream, use `emit ...;`"), + } + } + pub(crate) fn into_stream(self) -> ExecutionResult { let Spanned(value, span_range) = self; value.into_stream(Grouping::Flattened, span_range) diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 18a41456..90deff4c 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -48,12 +48,12 @@ impl ArrayValue { AnyValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; - std::mem::replace(&mut self.items[index], ().into_value()) + std::mem::replace(&mut self.items[index], ().into_any_value()) } AnyValueContent::Range(range) => { let range = Spanned(range, span_range).resolve_to_index_range(&self)?; let new_items: Vec<_> = self.items.drain(range).collect(); - new_items.into_value() + new_items.into_any_value() } _ => return span_range.type_err("The index must be an integer or a range"), }) @@ -170,9 +170,9 @@ impl ValuesEqual for ArrayValue { } } -impl IntoValue for Vec { - fn into_value(self) -> AnyValue { - ArrayValue { items: self }.into_value() +impl IntoAnyValue for Vec { + fn into_any_value(self) -> AnyValue { + ArrayValue { items: self }.into_any_value() } } @@ -190,8 +190,8 @@ define_type_features! { impl ArrayType, pub(crate) mod array_interface { pub(crate) mod methods { - fn push(mut this: Mutable, item: OwnedValue) -> ExecutionResult<()> { - this.items.push(item.into()); + fn push(mut this: Mutable, item: AnyValue) -> ExecutionResult<()> { + this.items.push(item); Ok(()) } @@ -201,11 +201,10 @@ define_type_features! { } } pub(crate) mod unary_operations { - [context] fn cast_singleton_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { - let mut this = this.into_inner(); + [context] fn cast_singleton_to_value(Spanned(mut this, span): Spanned) -> ExecutionResult { let length = this.items.len(); if length == 1 { - Ok(context.operation.evaluate(this.items.pop().unwrap().into_owned().spanned(span))?.0) + Ok(context.operation.evaluate(this.items.pop().unwrap().spanned(span))?.0) } else { context.operation.value_err(format!( "Only a singleton array can be cast to this value but the array has {} elements", diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 1457f027..c3b21c33 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -123,13 +123,13 @@ macro_rules! impl_float_operations { property_name: &str, ) -> Option { match property_name { - "MAX" => Some($float_type::MAX.into_value()), - "MIN" => Some($float_type::MIN.into_value()), - "MIN_POSITIVE" => Some($float_type::MIN_POSITIVE.into_value()), - "INFINITY" => Some($float_type::INFINITY.into_value()), - "NEG_INFINITY" => Some($float_type::NEG_INFINITY.into_value()), - "NAN" => Some($float_type::NAN.into_value()), - "EPSILON" => Some($float_type::EPSILON.into_value()), + "MAX" => Some($float_type::MAX.into_any_value()), + "MIN" => Some($float_type::MIN.into_any_value()), + "MIN_POSITIVE" => Some($float_type::MIN_POSITIVE.into_any_value()), + "INFINITY" => Some($float_type::INFINITY.into_any_value()), + "NEG_INFINITY" => Some($float_type::NEG_INFINITY.into_any_value()), + "NAN" => Some($float_type::NAN.into_any_value()), + "EPSILON" => Some($float_type::EPSILON.into_any_value()), _ => None, } } @@ -167,7 +167,7 @@ macro_rules! impl_resolvable_float_subtype { } } - impl ResolveAs> for Spanned { + impl ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { let span = self.span_range(); let float_value: FloatValue = self.resolve_as(resolution_target)?; diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index 863585dd..59d5a9c5 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -28,7 +28,7 @@ pub(crate) type IntegerValue = IntegerContent<'static, BeOwned>; pub(crate) type IntegerValueRef<'a> = IntegerContent<'a, BeRef>; impl IntegerValue { - pub(super) fn for_litint(lit: &syn::LitInt) -> ParseResult> { + pub(super) fn for_litint(lit: &syn::LitInt) -> ParseResult { Ok(match lit.suffix() { "" => IntegerContent::Untyped(UntypedInteger::new_from_lit_int(lit)?), "u8" => IntegerContent::U8(lit.base10_parse()?), @@ -48,8 +48,7 @@ impl IntegerValue { "The literal suffix {suffix} is not supported in preinterpret expressions" )); } - } - .into_owned()) + }) } pub(super) fn to_literal(self, span: Span) -> Literal { diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 57f9053d..ef053838 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -12,9 +12,8 @@ macro_rules! impl_int_operations { } pub(crate) mod unary_operations { $( - fn neg(Spanned(value, span): Spanned>) -> ExecutionResult<$integer_type> { + fn neg(Spanned(value, span): Spanned<$integer_type>) -> ExecutionResult<$integer_type> { ignore_all!($signed); // Include only for signed types - let value = value.into_inner(); match value.checked_neg() { Some(negated) => Ok(negated), None => span.value_err("Negating this value would overflow"), @@ -149,8 +148,8 @@ macro_rules! impl_int_operations { property_name: &str, ) -> Option { match property_name { - "MAX" => Some($integer_type::MAX.into_value()), - "MIN" => Some($integer_type::MIN.into_value()), + "MAX" => Some($integer_type::MAX.into_any_value()), + "MIN" => Some($integer_type::MIN.into_any_value()), _ => None, } } @@ -201,7 +200,7 @@ macro_rules! impl_resolvable_integer_subtype { } } - impl ResolveAs> for Spanned { + impl ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { let span = self.span_range(); let integer_value: IntegerValue = self.resolve_as(resolution_target)?; diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index 185350b7..58721af8 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -127,8 +127,7 @@ define_type_features! { pub(crate) mod methods { } pub(crate) mod unary_operations { - fn neg(Spanned(value, span): Spanned>) -> ExecutionResult { - let value = value.into_inner(); + fn neg(Spanned(value, span): Spanned) -> ExecutionResult { let input = value.into_fallback(); match input.checked_neg() { Some(negated) => Ok(UntypedInteger::from_fallback(negated)), diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index 6f16d336..e50fdf23 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -52,7 +52,7 @@ impl IteratorValue { let iterator = object .entries .into_iter() - .map(|(k, v)| vec![k.into_value(), v.value].into_value()) + .map(|(k, v)| vec![k.into_any_value(), v.value].into_any_value()) .collect::>() .into_iter(); Self::new_vec(iterator) @@ -66,7 +66,7 @@ impl IteratorValue { // https://internals.rust-lang.org/t/is-there-a-good-reason-why-string-has-no-into-chars/19496/5 let iterator = string .chars() - .map(|c| c.into_value()) + .map(|c| c.into_any_value()) .collect::>() .into_iter(); Self::new_vec(iterator) @@ -184,14 +184,14 @@ impl IteratorValue { } } -impl IntoValue for IteratorValueInner { - fn into_value(self) -> AnyValue { +impl IntoAnyValue for IteratorValueInner { + fn into_any_value(self) -> AnyValue { AnyValue::Iterator(IteratorValue::new(self)) } } -impl IntoValue for Box> { - fn into_value(self) -> AnyValue { +impl IntoAnyValue for Box> { + fn into_any_value(self) -> AnyValue { AnyValue::Iterator(IteratorValue::new_custom(self)) } } @@ -298,7 +298,7 @@ define_type_features! { fn next(mut this: Mutable) -> AnyValue { match this.next() { Some(value) => value, - None => ().into_value(), + None => ().into_any_value(), } } @@ -321,9 +321,9 @@ define_type_features! { } } pub(crate) mod unary_operations { - [context] fn cast_singleton_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { - match this.into_inner().singleton_value() { - Some(value) => Ok(context.operation.evaluate(Spanned(Owned::new(value), span))?.0), + [context] fn cast_singleton_to_value(Spanned(this, span): Spanned) -> ExecutionResult { + match this.singleton_value() { + Some(value) => Ok(context.operation.evaluate(Spanned(value, span))?.0), None => span.value_err("Only an iterator with one item can be cast to this value"), } } diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index acf9b4f5..10add3ad 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -55,7 +55,7 @@ impl ObjectValue { pub(crate) fn remove_or_none(&mut self, key: &str) -> AnyValue { match self.entries.remove(key) { Some(entry) => entry.value, - None => ().into_value(), + None => ().into_any_value(), } } @@ -121,7 +121,7 @@ impl ObjectValue { &mut entry .insert(ObjectEntry { key_span: key_span.join_into_span_else_start(), - value: ().into_value(), + value: ().into_any_value(), }) .value } else { @@ -258,8 +258,8 @@ impl Spanned<&ObjectValue> { } } -impl IntoValue for BTreeMap { - fn into_value(self) -> AnyValue { +impl IntoAnyValue for BTreeMap { + fn into_any_value(self) -> AnyValue { AnyValue::Object(ObjectValue { entries: self }) } } diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 55e8c243..89b1e996 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -143,8 +143,7 @@ define_type_features! { // Opens a group with the specified delimiter character ('(', '{', or '['). // Must be paired with `close`. - [context] fn open(this: Spanned>, Spanned(delimiter_char, char_span): Spanned>) -> ExecutionResult<()> { - let delimiter_char = delimiter_char.into_inner(); + [context] fn open(this: Spanned>, Spanned(delimiter_char, char_span): Spanned) -> ExecutionResult<()> { let delimiter = delimiter_from_open_char(delimiter_char) .ok_or_else(|| char_span.value_error(format!( "Invalid open delimiter '{}'. Expected '(', '{{', or '['", delimiter_char @@ -157,8 +156,7 @@ define_type_features! { // Closes the current group. Must be paired with a prior `open`. // The close character must match: ')' for '(', '}' for '{', ']' for '[' - [context] fn close(this: Spanned>, Spanned(delimiter_char, char_span): Spanned>) -> ExecutionResult<()> { - let delimiter_char = delimiter_char.into_inner(); + [context] fn close(this: Spanned>, Spanned(delimiter_char, char_span): Spanned) -> ExecutionResult<()> { let expected_delimiter = delimiter_from_close_char(delimiter_char) .ok_or_else(|| char_span.value_error(format!( "Invalid close delimiter '{}'. Expected ')', '}}', or ']'", delimiter_char @@ -195,7 +193,7 @@ define_type_features! { [context] fn inferred_literal(this: Spanned>) -> ExecutionResult { let literal = parser(this, context)?.parse()?; - Ok(AnyValue::for_literal(literal).into_value()) + Ok(AnyValue::for_literal(literal).into_any_value()) } [context] fn is_char(this: Spanned>) -> ExecutionResult { @@ -237,7 +235,7 @@ define_type_features! { [context] fn integer(this: Spanned>) -> ExecutionResult { let integer: syn::LitInt = parser(this, context)?.parse()?; - Ok(IntegerValue::for_litint(&integer)?.into_inner()) + Ok(IntegerValue::for_litint(&integer)?) } [context] fn is_float(this: Spanned>) -> ExecutionResult { @@ -272,27 +270,27 @@ impl_resolvable_argument_for! { } } -impl IntoValue for TokenTree { - fn into_value(self) -> AnyValue { - OutputStream::new_with(|s| s.push_raw_token_tree(self)).into_value() +impl IntoAnyValue for TokenTree { + fn into_any_value(self) -> AnyValue { + OutputStream::new_with(|s| s.push_raw_token_tree(self)).into_any_value() } } -impl IntoValue for Ident { - fn into_value(self) -> AnyValue { - OutputStream::new_with(|s| s.push_ident(self)).into_value() +impl IntoAnyValue for Ident { + fn into_any_value(self) -> AnyValue { + OutputStream::new_with(|s| s.push_ident(self)).into_any_value() } } -impl IntoValue for Punct { - fn into_value(self) -> AnyValue { - OutputStream::new_with(|s| s.push_punct(self)).into_value() +impl IntoAnyValue for Punct { + fn into_any_value(self) -> AnyValue { + OutputStream::new_with(|s| s.push_punct(self)).into_any_value() } } -impl IntoValue for Literal { - fn into_value(self) -> AnyValue { - OutputStream::new_with(|s| s.push_literal(self)).into_value() +impl IntoAnyValue for Literal { + fn into_any_value(self) -> AnyValue { + OutputStream::new_with(|s| s.push_literal(self)).into_any_value() } } @@ -343,7 +341,7 @@ impl Evaluate for ParseTemplateLiteral { parser.parse_with(interpreter, |interpreter| self.content.consume(interpreter))?; ownership - .map_from_owned(Spanned(().into_owned_value(), self.span_range())) + .map_from_owned(Spanned(().into_any_value(), self.span_range())) .map(|spanned| spanned.0) } } diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index 3cbfe4ef..428c9edb 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -372,8 +372,8 @@ impl RangeValueInner { } } -impl IntoValue for RangeValueInner { - fn into_value(self) -> AnyValue { +impl IntoAnyValue for RangeValueInner { + fn into_any_value(self) -> AnyValue { AnyValue::Range(RangeValue { inner: Box::new(self), }) @@ -396,8 +396,8 @@ define_type_features! { pub(crate) mod methods { } pub(crate) mod unary_operations { - [context] fn cast_via_iterator(Spanned(this, span): Spanned>) -> ExecutionResult { - let this_iterator = this.try_map(IteratorValue::new_for_range)?; + [context] fn cast_via_iterator(Spanned(this, span): Spanned) -> ExecutionResult { + let this_iterator = IteratorValue::new_for_range(this)?; Ok(context.operation.evaluate(Spanned(this_iterator, span))?.0) } } @@ -434,7 +434,7 @@ pub(super) enum IterableRangeOf { fn resolve_range + ResolvableRange>( start: T, dots: syn::RangeLimits, - end: Option>, + end: Option>, ) -> ExecutionResult>> { let definition = match (end, dots) { (Some(end), dots) => { @@ -460,11 +460,9 @@ impl IterableRangeOf { self, ) -> ExecutionResult>> { let (start, dots, end) = match self { - Self::RangeFromTo { start, dots, end } => ( - start, - dots, - Some(end.into_owned().spanned(dots.span_range())), - ), + Self::RangeFromTo { start, dots, end } => { + (start, dots, Some(end.spanned(dots.span_range()))) + } Self::RangeFrom { start, dots } => (start, RangeLimits::HalfOpen(dots), None), }; match start { @@ -507,17 +505,19 @@ impl ResolvableRange for UntypedInteger { let end = end.into_fallback(); Ok(match dots { syn::RangeLimits::HalfOpen { .. } => Box::new( - (start..end).map(move |x| UntypedInteger::from_fallback(x).into_value()), + (start..end) + .map(move |x| UntypedInteger::from_fallback(x).into_any_value()), ), syn::RangeLimits::Closed { .. } => Box::new( - (start..=end).map(move |x| UntypedInteger::from_fallback(x).into_value()), + (start..=end) + .map(move |x| UntypedInteger::from_fallback(x).into_any_value()), ), }) } IterableRangeOf::RangeFrom { start, .. } => { let start = start.into_fallback(); Ok(Box::new((start..).map(move |x| { - UntypedInteger::from_fallback(x).into_value() + UntypedInteger::from_fallback(x).into_any_value() }))) } } @@ -534,15 +534,15 @@ macro_rules! define_range_resolvers { IterableRangeOf::RangeFromTo { start, dots, end } => { Ok(match dots { syn::RangeLimits::HalfOpen { .. } => { - Box::new((start..end).map(move |x| x.into_value())) + Box::new((start..end).map(move |x| x.into_any_value())) } syn::RangeLimits::Closed { .. } => { - Box::new((start..=end).map(move |x| x.into_value())) + Box::new((start..=end).map(move |x| x.into_any_value())) } }) }, IterableRangeOf::RangeFrom { start, .. } => { - Ok(Box::new((start..).map(move |x| x.into_value()))) + Ok(Box::new((start..).map(move |x| x.into_any_value()))) }, } } diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index ba159e28..b1d427ac 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -97,9 +97,9 @@ impl ValuesEqual for OutputStream { } } -impl IntoValue for TokenStream { - fn into_value(self) -> AnyValue { - OutputStream::raw(self).into_value() +impl IntoAnyValue for TokenStream { + fn into_any_value(self) -> AnyValue { + OutputStream::raw(self).into_any_value() } } @@ -172,7 +172,7 @@ define_type_features! { [context] fn to_literal(this: Spanned>) -> ExecutionResult { let string = this.concat_content(&ConcatBehaviour::literal(this.span_range())); let literal = string_interface::methods::to_literal(context, string.as_str().into_spanned_ref(this.span_range()))?; - Ok(AnyValue::for_literal(literal).into_value()) + Ok(AnyValue::for_literal(literal).into_any_value()) } // CORE METHODS @@ -222,8 +222,8 @@ define_type_features! { } } - [context] fn reinterpret_as_run(Spanned(this, span_range): Spanned>) -> ExecutionResult { - let source = this.into_inner().into_token_stream(); + [context] fn reinterpret_as_run(Spanned(this, span_range): Spanned) -> ExecutionResult { + let source = this.into_token_stream(); let (reparsed, scope_definitions) = source.source_parse_and_analyze(ExpressionBlockContent::parse, ExpressionBlockContent::control_flow_pass)?; let mut inner_interpreter = Interpreter::new(scope_definitions); let return_value = reparsed.evaluate_spanned(&mut inner_interpreter, span_range, RequestedOwnership::owned())?.expect_owned(); @@ -233,8 +233,8 @@ define_type_features! { Ok(return_value.0) } - fn reinterpret_as_stream(Spanned(this, span_range): Spanned>) -> ExecutionResult { - let source = this.into_inner().into_token_stream(); + fn reinterpret_as_stream(Spanned(this, span_range): Spanned) -> ExecutionResult { + let source = this.into_token_stream(); let (reparsed, scope_definitions) = source.source_parse_and_analyze( |input| SourceStream::parse_with_span(input, span_range.span_from_join_else_start()), SourceStream::control_flow_pass, @@ -245,14 +245,13 @@ define_type_features! { } } pub(crate) mod unary_operations { - [context] fn cast_coerced_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { - let this = this.into_inner(); + [context] fn cast_coerced_to_value(Spanned(this, span): Spanned) -> ExecutionResult { let coerced = this.coerce_into_value(); if let AnyValue::Stream(_) = &coerced { return span.value_err("The stream could not be coerced into a single value"); } // Re-run the cast operation on the coerced value - Ok(context.operation.evaluate(Spanned(coerced.into_owned(), span))?.0) + Ok(context.operation.evaluate(Spanned(coerced, span))?.0) } } pub(crate) mod binary_operations { @@ -522,26 +521,26 @@ impl Interpret for ConcatenatedStreamLiteral { .unwrap_or_else(|| self.span_range()) .join_into_span_else_start(); let value = match self.kind { - ConcatenatedStreamLiteralKind::String => string.into_value(), + ConcatenatedStreamLiteralKind::String => string.into_any_value(), ConcatenatedStreamLiteralKind::Ident => { let str = &string; - string_to_ident(str, self, ident_span)?.into_value() + string_to_ident(str, self, ident_span)?.into_any_value() } ConcatenatedStreamLiteralKind::IdentCamel => { let str = &string_conversion::to_upper_camel_case(&string); - string_to_ident(str, self, ident_span)?.into_value() + string_to_ident(str, self, ident_span)?.into_any_value() } ConcatenatedStreamLiteralKind::IdentSnake => { let str = &string_conversion::to_lower_snake_case(&string); - string_to_ident(str, self, ident_span)?.into_value() + string_to_ident(str, self, ident_span)?.into_any_value() } ConcatenatedStreamLiteralKind::IdentUpperSnake => { let str = &string_conversion::to_upper_snake_case(&string); - string_to_ident(str, self, ident_span)?.into_value() + string_to_ident(str, self, ident_span)?.into_any_value() } ConcatenatedStreamLiteralKind::Literal => { let str = &string; - string_to_literal(str, self, ident_span)?.into_value() + string_to_literal(str, self, ident_span)?.into_any_value() } }; value.as_ref_value().output_to( diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index 44bc0f92..f620526f 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -42,9 +42,9 @@ impl ValuesEqual for String { } } -impl IntoValue for &str { - fn into_value(self) -> AnyValue { - self.to_string().into_value() +impl IntoAnyValue for &str { + fn into_any_value(self) -> AnyValue { + self.to_string().into_any_value() } } diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index cf2104d2..e8053896 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -50,7 +50,7 @@ impl VariableState { } return Ok(Spanned( LateBoundValue::Owned(LateBoundOwnedValue { - owned: Owned(ref_cell.into_inner()), + owned: ref_cell.into_inner(), is_from_last_use: true, }), span_range, @@ -127,18 +127,18 @@ pub(crate) struct VariableBinding { impl VariableBinding { /// Gets the cloned expression value /// This only works if the value can be transparently cloned - pub(crate) fn into_transparently_cloned(self) -> ExecutionResult { + pub(crate) fn into_transparently_cloned(self) -> ExecutionResult { let span_range = self.variable_span.span_range(); let shared = self.into_shared()?; let value = shared.as_ref().try_transparent_clone(span_range)?; - Ok(Owned(value)) + Ok(value) } - fn into_mut(self) -> ExecutionResult { + fn into_mut(self) -> ExecutionResult> { MutableValue::new_from_variable(self).map_err(ExecutionInterrupt::ownership_error) } - fn into_shared(self) -> ExecutionResult { + fn into_shared(self) -> ExecutionResult> { SharedValue::new_from_variable(self).map_err(ExecutionInterrupt::ownership_error) } @@ -159,7 +159,7 @@ impl VariableBinding { } pub(crate) struct LateBoundOwnedValue { - pub(crate) owned: OwnedValue, + pub(crate) owned: Owned, pub(crate) is_from_last_use: bool, } @@ -194,7 +194,7 @@ pub(crate) enum LateBoundValue { /// A copy-on-write value that can be converted to an owned value CopyOnWrite(CopyOnWriteValue), /// A mutable reference - Mutable(MutableValue), + Mutable(Mutable), /// A shared reference where mutable access failed for a specific reason Shared(LateBoundSharedValue), } @@ -208,9 +208,9 @@ impl Spanned { impl LateBoundValue { pub(crate) fn map_any( self, - map_shared: impl FnOnce(SharedValue) -> ExecutionResult, - map_mutable: impl FnOnce(MutableValue) -> ExecutionResult, - map_owned: impl FnOnce(OwnedValue) -> ExecutionResult, + map_shared: impl FnOnce(Shared) -> ExecutionResult>, + map_mutable: impl FnOnce(Mutable) -> ExecutionResult>, + map_owned: impl FnOnce(Owned) -> ExecutionResult>, ) -> ExecutionResult { Ok(match self { LateBoundValue::Owned(owned) => LateBoundValue::Owned(LateBoundOwnedValue { @@ -249,79 +249,6 @@ impl Deref for LateBoundValue { } } -pub(crate) type OwnedValue = Owned; - -/// A semantic wrapper for floating owned values. -/// -/// Can be destructured as: `Owned(value): Owned` -/// -/// If you need span information, wrap with `Spanned>`. For example, for `x.y[4]`, this would capture both: -/// * The output owned value -/// * The lexical span of the tokens `x.y[4]` -pub(crate) struct Owned(pub(crate) T); - -#[allow(unused)] -impl Owned { - pub(crate) fn new(value: T) -> Self { - Owned(value) - } - - pub(crate) fn into_inner(self) -> T { - self.0 - } - - pub(crate) fn map(self, value_map: impl FnOnce(T) -> V) -> Owned { - Owned(value_map(self.0)) - } - - pub(crate) fn try_map( - self, - value_map: impl FnOnce(T) -> Result, - ) -> Result, E> { - Ok(Owned(value_map(self.0)?)) - } -} - -impl Spanned { - pub(crate) fn into_statement_result(self) -> ExecutionResult<()> { - let Spanned(value, span_range) = self; - match value.0 { - AnyValueContent::None(_) => Ok(()), - _ => span_range.control_flow_err("A non-returning statement must not return a value. If you wish to explicitly discard the expression's result, use `let _ = ...;`. Alternatively, If you wish to output the value into the parent token stream, use `emit ...;`"), - } - } -} - -impl Owned { - pub(crate) fn into_value(self) -> AnyValue { - self.0.into_value() - } - - pub(crate) fn into_owned_value(self) -> OwnedValue { - Owned(self.into_value()) - } -} - -impl From for AnyValue { - fn from(value: OwnedValue) -> Self { - value.0 - } -} - -impl Deref for Owned { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Owned { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - pub(crate) type MutableValue = Mutable; pub(crate) type AssigneeValue = Assignee; @@ -332,8 +259,8 @@ pub(crate) type AssigneeValue = Assignee; pub(crate) struct Assignee(pub Mutable); impl AssigneeValue { - pub(crate) fn set(&mut self, content: impl IntoValue) { - *self.0 .0 = content.into_value(); + pub(crate) fn set(&mut self, content: impl IntoAnyValue) { + *self.0 .0 = content.into_any_value(); } } @@ -403,16 +330,16 @@ impl Spanned<&mut Mutable> { } impl Spanned> { - pub(crate) fn transparent_clone(&self) -> ExecutionResult { + pub(crate) fn transparent_clone(&self) -> ExecutionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; - Ok(Owned(value)) + Ok(value) } } impl Mutable { - pub(crate) fn new_from_owned(value: OwnedValue) -> Self { + pub(crate) fn new_from_owned(value: AnyValue) -> Self { // Unwrap is safe because it's a new refcell - Mutable(MutableSubRcRefCell::new(Rc::new(RefCell::new(value.0))).unwrap()) + Mutable(MutableSubRcRefCell::new(Rc::new(RefCell::new(value))).unwrap()) } fn new_from_variable(reference: VariableBinding) -> syn::Result { @@ -499,20 +426,20 @@ impl Spanned<&mut Shared> { } impl Spanned> { - pub(crate) fn transparent_clone(&self) -> ExecutionResult { + pub(crate) fn transparent_clone(&self) -> ExecutionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; - Ok(Owned(value)) + Ok(value) } } impl Shared { - pub(crate) fn new_from_owned(value: OwnedValue) -> Self { + pub(crate) fn new_from_owned(value: AnyValue) -> Self { // Unwrap is safe because it's a new refcell - Shared(SharedSubRcRefCell::new(Rc::new(RefCell::new(value.0))).unwrap()) + Shared(SharedSubRcRefCell::new(Rc::new(RefCell::new(value))).unwrap()) } - pub(crate) fn infallible_clone(&self) -> OwnedValue { - Owned(self.0.clone()) + pub(crate) fn infallible_clone(&self) -> AnyValue { + self.0.clone() } fn new_from_variable(reference: VariableBinding) -> syn::Result { @@ -577,10 +504,10 @@ impl CopyOnWrite { map: impl FnOnce(T::Owned) -> Result, ) -> Result { match self.inner { - CopyOnWriteInner::Owned(Owned(value)) => match map(value) { + CopyOnWriteInner::Owned(value) => match map(value) { Ok(mapped) => Ok(mapped), Err(other) => Err(Self { - inner: CopyOnWriteInner::Owned(Owned::new(other)), + inner: CopyOnWriteInner::Owned(other), }), }, other => Err(Self { inner: other }), @@ -669,7 +596,7 @@ impl Deref for CopyOnWrite { fn deref(&self) -> &T { match self.inner { - CopyOnWriteInner::Owned(ref owned) => (**owned).borrow(), + CopyOnWriteInner::Owned(ref owned) => (*owned).borrow(), CopyOnWriteInner::SharedWithInfallibleCloning(ref shared) => shared.as_ref(), CopyOnWriteInner::SharedWithTransparentCloning(ref shared) => shared.as_ref(), } @@ -678,7 +605,7 @@ impl Deref for CopyOnWrite { impl CopyOnWrite { /// Converts to owned, cloning if necessary - pub(crate) fn clone_to_owned_infallible(self) -> OwnedValue { + pub(crate) fn clone_to_owned_infallible(self) -> Owned { match self.inner { CopyOnWriteInner::Owned(owned) => owned, CopyOnWriteInner::SharedWithInfallibleCloning(shared) => shared.infallible_clone(), @@ -690,13 +617,13 @@ impl CopyOnWrite { pub(crate) fn clone_to_owned_transparently( self, span: SpanRange, - ) -> ExecutionResult { + ) -> ExecutionResult> { match self.inner { CopyOnWriteInner::Owned(owned) => Ok(owned), CopyOnWriteInner::SharedWithInfallibleCloning(shared) => Ok(shared.infallible_clone()), CopyOnWriteInner::SharedWithTransparentCloning(shared) => { let value = shared.as_ref().try_transparent_clone(span)?; - Ok(Owned(value)) + Ok(value) } } } diff --git a/src/interpretation/output_stream.rs b/src/interpretation/output_stream.rs index e60b862e..aedc470c 100644 --- a/src/interpretation/output_stream.rs +++ b/src/interpretation/output_stream.rs @@ -123,9 +123,9 @@ impl OutputStream { pub(crate) fn coerce_into_value(self) -> AnyValue { let parse_result = self.clone().parse_as::(); match parse_result { - Ok(syn_lit) => AnyValue::for_syn_lit(syn_lit).into_inner(), + Ok(syn_lit) => AnyValue::for_syn_lit(syn_lit), // Keep as stream otherwise - Err(_) => self.into_value(), + Err(_) => self.into_any_value(), } } diff --git a/src/interpretation/variable.rs b/src/interpretation/variable.rs index 40a94104..72ee8431 100644 --- a/src/interpretation/variable.rs +++ b/src/interpretation/variable.rs @@ -65,8 +65,8 @@ impl ParseSource for VariableDefinition { } impl VariableDefinition { - pub(crate) fn define(&self, interpreter: &mut Interpreter, value_source: impl IntoValue) { - interpreter.define_variable(self.id, value_source.into_value()); + pub(crate) fn define(&self, interpreter: &mut Interpreter, value_source: impl IntoAnyValue) { + interpreter.define_variable(self.id, value_source.into_any_value()); } } diff --git a/src/lib.rs b/src/lib.rs index b3362b4b..6b24f889 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,7 +461,7 @@ fn preinterpret_run_internal(input: TokenStream) -> SynResult { let entry_span = Span::call_site().span_range(); let returned_stream = content .evaluate_spanned(&mut interpreter, entry_span, RequestedOwnership::owned()) - .and_then(|x| x.expect_owned().map(|owned| owned.0).into_stream()) + .and_then(|x| x.expect_owned().into_stream()) .convert_to_final_result()?; let mut output_stream = interpreter.complete(); diff --git a/src/misc/errors.rs b/src/misc/errors.rs index f5a05057..6dd464c9 100644 --- a/src/misc/errors.rs +++ b/src/misc/errors.rs @@ -281,7 +281,7 @@ impl std::fmt::Debug for ControlFlowInterrupt { impl ControlFlowInterrupt { pub(crate) fn new_break( target_catch_location: CatchLocationId, - value: Option, + value: Option, ) -> Self { ControlFlowInterrupt::Break(BreakInterrupt { target_catch_location, @@ -316,7 +316,7 @@ impl ControlFlowInterrupt { pub(crate) struct BreakInterrupt { target_catch_location: CatchLocationId, - value: Option, + value: Option, } impl BreakInterrupt { @@ -327,7 +327,7 @@ impl BreakInterrupt { ) -> ExecutionResult { let value = match self.value { Some(value) => value, - None => ().into_owned_value(), + None => ().into_any_value(), }; ownership .map_from_owned(Spanned(value, span_range)) diff --git a/src/misc/field_inputs.rs b/src/misc/field_inputs.rs index fbeea261..1b63d11e 100644 --- a/src/misc/field_inputs.rs +++ b/src/misc/field_inputs.rs @@ -69,7 +69,7 @@ macro_rules! define_typed_object { impl ResolvableOwned for $model { fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { - Self::from_object_value(ObjectValue::resolve_spanned_owned_from_value(value, context)?) + Self::from_object_value(ObjectValue::resolve_spanned_from_value(value, context)?) } } @@ -94,8 +94,7 @@ macro_rules! define_typed_object { } impl $model { - fn from_object_value(Spanned(object, span_range): Spanned>) -> ExecutionResult { - let mut object = object.into_inner(); + fn from_object_value(Spanned(mut object, span_range): Spanned) -> ExecutionResult { (&object).spanned(span_range).validate(&Self::validation())?; Ok($model { $( @@ -109,14 +108,14 @@ macro_rules! define_typed_object { { // Need to return the $optional_field_type match optional { - Some(value) => ResolveAs::<$optional_field_type>::resolve_as(Spanned(value.into_owned(), span_range), stringify!($optional_field))?, + Some(value) => ResolveAs::<$optional_field_type>::resolve_as(Spanned(value, span_range), stringify!($optional_field))?, None => $($optional_field_default)?, } } { // Need to return Option<$optional_field_type> match optional { - Some(value) => Some(ResolveAs::<$optional_field_type>::resolve_as(Spanned(value.into_owned(), span_range), stringify!($optional_field))?), + Some(value) => Some(ResolveAs::<$optional_field_type>::resolve_as(Spanned(value, span_range), stringify!($optional_field))?), None => None, } } diff --git a/src/misc/iterators.rs b/src/misc/iterators.rs index cb2e3aed..591f1337 100644 --- a/src/misc/iterators.rs +++ b/src/misc/iterators.rs @@ -165,7 +165,7 @@ impl ZipIterators { for iter in iterators.iter_mut() { inner.push(iter.next().unwrap()); } - output.push(inner.into_value()); + output.push(inner.into_any_value()); } } ZipIterators::Object(iterators, _) => { @@ -181,7 +181,7 @@ impl ZipIterators { }, ); } - output.push(inner.into_value()); + output.push(inner.into_any_value()); } } } @@ -318,7 +318,7 @@ pub(crate) fn handle_split( while !input.is_empty() { current_item.push_raw_token_tree(input.parse()?); let complete_item = core::mem::replace(&mut current_item, OutputStream::new()); - output.push(complete_item.into_value()); + output.push(complete_item.into_any_value()); } return Ok(ArrayValue::new(output)); } @@ -338,12 +338,12 @@ pub(crate) fn handle_split( input.advance_to(&separator_fork); if !current_item.is_empty() || !drop_empty_next { let complete_item = core::mem::replace(&mut current_item, OutputStream::new()); - output.push(complete_item.into_value()); + output.push(complete_item.into_any_value()); } drop_empty_next = settings.drop_empty_middle; } if !current_item.is_empty() || !settings.drop_empty_end { - output.push(current_item.into_value()); + output.push(current_item.into_any_value()); } Ok(ArrayValue::new(output)) }) diff --git a/src/misc/mod.rs b/src/misc/mod.rs index 4e053fe2..dd3a9973 100644 --- a/src/misc/mod.rs +++ b/src/misc/mod.rs @@ -38,8 +38,8 @@ pub(crate) fn print_if_slow( // Equivalent to `!` but stable in our MSRV pub(crate) enum Never {} -impl IntoValue for Never { - fn into_value(self) -> AnyValue { +impl IntoAnyValue for Never { + fn into_any_value(self) -> AnyValue { match self {} } } From 87ccd83fbe14d579071c4ed82699e02d834fc8d1 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 8 Jan 2026 16:14:33 +0100 Subject: [PATCH 33/58] tweak: Prepare type aliases for Shared/Mutable/Assignee migration --- plans/TODO.md | 7 ++-- src/expressions/evaluation/evaluator.rs | 24 ++++++------- src/expressions/evaluation/value_frames.rs | 30 ++++++++-------- src/expressions/type_resolution/arguments.rs | 4 +-- src/expressions/type_resolution/outputs.rs | 10 +++--- src/expressions/values/any_value.rs | 10 +++++- src/interpretation/bindings.rs | 36 ++++++++++---------- 7 files changed, 63 insertions(+), 58 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 8c232e32..c6b279f4 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -246,11 +246,8 @@ First, read the @./2025-11-vision.md - [x] Remove `OwnedValue` - [x] Get rid of `Owned` - [ ] Stage 2 of the form migration: - - [ ] Improve mappers: - - [ ] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - - [ ] 6 methods... `map_content`, `map_content_ref`, `map_content_mut` - - [ ] ... and a `ReduceMapper::::map(content, |x| -> y)` - - [ ] ... Probably remove e.g. `map_with` etc on actual? + - [x] Attempt to improve mappers: + - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. - [ ] Migrate `Shared`, `Mutable`, `Assignee` - [ ] Stage 3 - [ ] Migrate `CopyOnWrite` and relevant interconversions diff --git a/src/expressions/evaluation/evaluator.rs b/src/expressions/evaluation/evaluator.rs index 75422457..ca3601d6 100644 --- a/src/expressions/evaluation/evaluator.rs +++ b/src/expressions/evaluation/evaluator.rs @@ -146,9 +146,9 @@ impl From for NextAction { pub(crate) enum RequestedValue { // RequestedOwnership::Concrete(_) // ------------------------------- - Owned(Owned), - Shared(Shared), - Mutable(Mutable), + Owned(AnyValueOwned), + Shared(AnyValueShared), + Mutable(AnyValueMutable), CopyOnWrite(CopyOnWriteValue), Assignee(AssigneeValue), @@ -162,21 +162,21 @@ pub(crate) enum RequestedValue { } impl RequestedValue { - pub(crate) fn expect_owned(self) -> Owned { + pub(crate) fn expect_owned(self) -> AnyValueOwned { match self { RequestedValue::Owned(value) => value, _ => panic!("expect_owned() called on non-owned RequestedValue"), } } - pub(crate) fn expect_shared(self) -> Shared { + pub(crate) fn expect_shared(self) -> AnyValueShared { match self { RequestedValue::Shared(shared) => shared, _ => panic!("expect_shared() called on non-shared RequestedValue"), } } - pub(super) fn expect_assignee(self) -> Assignee { + pub(super) fn expect_assignee(self) -> AnyValueAssignee { match self { RequestedValue::Assignee(assignee) => assignee, _ => panic!("expect_assignee() called on non-assignee RequestedValue"), @@ -214,9 +214,9 @@ impl RequestedValue { pub(crate) fn expect_any_value_and_map( self, - map_shared: impl FnOnce(Shared) -> ExecutionResult>, - map_mutable: impl FnOnce(Mutable) -> ExecutionResult>, - map_owned: impl FnOnce(Owned) -> ExecutionResult>, + map_shared: impl FnOnce(AnyValueShared) -> ExecutionResult, + map_mutable: impl FnOnce(AnyValueMutable) -> ExecutionResult, + map_owned: impl FnOnce(AnyValueOwned) -> ExecutionResult, ) -> ExecutionResult { Ok(match self { RequestedValue::LateBound(late_bound) => { @@ -241,17 +241,17 @@ impl RequestedValue { #[allow(unused)] impl Spanned { #[inline] - pub(crate) fn expect_owned(self) -> Spanned> { + pub(crate) fn expect_owned(self) -> Spanned { self.map(|v| v.expect_owned()) } #[inline] - pub(crate) fn expect_shared(self) -> Spanned> { + pub(crate) fn expect_shared(self) -> Spanned { self.map(|v| v.expect_shared()) } #[inline] - pub(crate) fn expect_assignee(self) -> Spanned> { + pub(crate) fn expect_assignee(self) -> Spanned { self.map(|v| v.expect_assignee()) } diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 84b96d29..8a27d767 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -6,15 +6,15 @@ use super::*; /// It is typically paired with an [`ArgumentOwnership`] (maybe wrapped in a [`RequestedOwnership`]) /// which indicates what ownership type to resolve to. pub(crate) enum ArgumentValue { - Owned(Owned), + Owned(AnyValueOwned), CopyOnWrite(CopyOnWriteValue), - Mutable(Mutable), - Assignee(Assignee), - Shared(Shared), + Mutable(AnyValueMutable), + Assignee(AnyValueAssignee), + Shared(AnyValueShared), } impl ArgumentValue { - pub(crate) fn expect_owned(self) -> Owned { + pub(crate) fn expect_owned(self) -> AnyValueOwned { match self { ArgumentValue::Owned(value) => value, _ => panic!("expect_owned() called on a non-owned ArgumentValue"), @@ -28,21 +28,21 @@ impl ArgumentValue { } } - pub(crate) fn expect_mutable(self) -> Mutable { + pub(crate) fn expect_mutable(self) -> AnyValueMutable { match self { ArgumentValue::Mutable(value) => value, _ => panic!("expect_mutable() called on a non-mutable ArgumentValue"), } } - pub(crate) fn expect_assignee(self) -> Assignee { + pub(crate) fn expect_assignee(self) -> AnyValueAssignee { match self { ArgumentValue::Assignee(value) => value, _ => panic!("expect_assignee() called on a non-assignee ArgumentValue"), } } - pub(crate) fn expect_shared(self) -> Shared { + pub(crate) fn expect_shared(self) -> AnyValueShared { match self { ArgumentValue::Shared(value) => value, _ => panic!("expect_shared() called on a non-shared ArgumentValue"), @@ -52,22 +52,22 @@ impl ArgumentValue { impl Spanned { #[inline] - pub(crate) fn expect_owned(self) -> Spanned> { + pub(crate) fn expect_owned(self) -> Spanned { self.map(|value| value.expect_owned()) } #[inline] - pub(crate) fn expect_mutable(self) -> Spanned> { + pub(crate) fn expect_mutable(self) -> Spanned { self.map(|value| value.expect_mutable()) } #[inline] - pub(crate) fn expect_assignee(self) -> Spanned> { + pub(crate) fn expect_assignee(self) -> Spanned { self.map(|value| value.expect_assignee()) } #[inline] - pub(crate) fn expect_shared(self) -> Spanned> { + pub(crate) fn expect_shared(self) -> Spanned { self.map(|value| value.expect_shared()) } } @@ -277,7 +277,7 @@ impl RequestedOwnership { pub(crate) fn map_from_mutable( &self, - Spanned(mutable, span): Spanned>, + Spanned(mutable, span): Spanned, ) -> ExecutionResult> { Ok(Spanned( match self { @@ -466,7 +466,7 @@ impl ArgumentOwnership { pub(crate) fn map_from_mutable( &self, - spanned_mutable: Spanned>, + spanned_mutable: Spanned, ) -> ExecutionResult { self.map_from_mutable_inner(spanned_mutable, false) } @@ -480,7 +480,7 @@ impl ArgumentOwnership { fn map_from_mutable_inner( &self, - Spanned(mutable, span): Spanned>, + Spanned(mutable, span): Spanned, is_late_bound: bool, ) -> ExecutionResult { match self { diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index b8b3b5eb..9fa5e198 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -165,7 +165,7 @@ impl> ResolveAs for Spanned { } } -impl + ?Sized> ResolveAs> for Spanned> { +impl + ?Sized> ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_shared(self, resolution_target) } @@ -185,7 +185,7 @@ impl<'a, T: ResolvableShared + ?Sized> ResolveAs> } } -impl + ?Sized> ResolveAs> for Spanned> { +impl + ?Sized> ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_mutable(self, resolution_target) } diff --git a/src/expressions/type_resolution/outputs.rs b/src/expressions/type_resolution/outputs.rs index 3bc93c96..f0f9b493 100644 --- a/src/expressions/type_resolution/outputs.rs +++ b/src/expressions/type_resolution/outputs.rs @@ -5,10 +5,10 @@ use super::*; /// /// See also [`RequestedValue`] for values that have been fully evaluated. pub(crate) enum ReturnedValue { - Owned(Owned), + Owned(AnyValueOwned), CopyOnWrite(CopyOnWriteValue), - Mutable(Mutable), - Shared(Shared), + Mutable(AnyValueMutable), + Shared(AnyValueShared), } // TODO: Find some way to selectively enable only on MSRV (e.g. following the build.rs feature flag pattern) @@ -26,13 +26,13 @@ impl IsReturnable for ReturnedValue { } } -impl IsReturnable for Shared { +impl IsReturnable for AnyValueShared { fn to_returned_value(self) -> ExecutionResult { Ok(ReturnedValue::Shared(self)) } } -impl IsReturnable for Mutable { +impl IsReturnable for AnyValueMutable { fn to_returned_value(self) -> ExecutionResult { Ok(ReturnedValue::Mutable(self)) } diff --git a/src/expressions/values/any_value.rs b/src/expressions/values/any_value.rs index b9b5d7f9..632865c1 100644 --- a/src/expressions/values/any_value.rs +++ b/src/expressions/values/any_value.rs @@ -1,8 +1,16 @@ use super::*; pub(crate) type AnyValue = AnyValueContent<'static, BeOwned>; +/// For symmetry +pub(crate) type AnyValueOwned = AnyValue; pub(crate) type AnyValueRef<'a> = AnyValueContent<'a, BeRef>; pub(crate) type AnyValueMut<'a> = AnyValueContent<'a, BeMut>; +pub(crate) type AnyValueShared = Shared; +pub(crate) type AnyValueMutable = Mutable; +pub(crate) type AnyValueAssignee = Assignee; +// pub(crate) type AnyValueShared = AnyValueContent<'static, BeShared>; +// pub(crate) type AnyValueMutable = AnyValueContent<'static, BeMutable>; +// pub(crate) type AnyValueAssignee = AnyValueContent<'static, BeAssignee>; define_parent_type! { pub(crate) AnyType, @@ -38,7 +46,7 @@ define_type_features! { this.clone_to_owned_infallible() } - fn as_mut(Spanned(this, span): Spanned) -> ExecutionResult> { + fn as_mut(Spanned(this, span): Spanned) -> ExecutionResult { Ok(match this { ArgumentValue::Owned(owned) => Mutable::new_from_owned(owned), ArgumentValue::CopyOnWrite(copy_on_write) => ArgumentOwnership::Mutable diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index e8053896..463b0725 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -134,11 +134,11 @@ impl VariableBinding { Ok(value) } - fn into_mut(self) -> ExecutionResult> { + fn into_mut(self) -> ExecutionResult { MutableValue::new_from_variable(self).map_err(ExecutionInterrupt::ownership_error) } - fn into_shared(self) -> ExecutionResult> { + fn into_shared(self) -> ExecutionResult { SharedValue::new_from_variable(self).map_err(ExecutionInterrupt::ownership_error) } @@ -159,7 +159,7 @@ impl VariableBinding { } pub(crate) struct LateBoundOwnedValue { - pub(crate) owned: Owned, + pub(crate) owned: AnyValueOwned, pub(crate) is_from_last_use: bool, } @@ -194,7 +194,7 @@ pub(crate) enum LateBoundValue { /// A copy-on-write value that can be converted to an owned value CopyOnWrite(CopyOnWriteValue), /// A mutable reference - Mutable(Mutable), + Mutable(AnyValueMutable), /// A shared reference where mutable access failed for a specific reason Shared(LateBoundSharedValue), } @@ -208,9 +208,9 @@ impl Spanned { impl LateBoundValue { pub(crate) fn map_any( self, - map_shared: impl FnOnce(Shared) -> ExecutionResult>, - map_mutable: impl FnOnce(Mutable) -> ExecutionResult>, - map_owned: impl FnOnce(Owned) -> ExecutionResult>, + map_shared: impl FnOnce(AnyValueShared) -> ExecutionResult, + map_mutable: impl FnOnce(AnyValueMutable) -> ExecutionResult, + map_owned: impl FnOnce(AnyValueOwned) -> ExecutionResult, ) -> ExecutionResult { Ok(match self { LateBoundValue::Owned(owned) => LateBoundValue::Owned(LateBoundOwnedValue { @@ -249,8 +249,8 @@ impl Deref for LateBoundValue { } } -pub(crate) type MutableValue = Mutable; -pub(crate) type AssigneeValue = Assignee; +pub(crate) type MutableValue = AnyValueMutable; +pub(crate) type AssigneeValue = AnyValueAssignee; /// A binding of a unique (mutable) reference to a value. /// See [`ArgumentOwnership::Assignee`] for more details. @@ -309,7 +309,7 @@ impl Mutable { pub(crate) static MUTABLE_ERROR_MESSAGE: &str = "The variable cannot be modified as it is already being modified"; -impl Spanned<&mut Mutable> { +impl Spanned<&mut AnyValueMutable> { /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -329,14 +329,14 @@ impl Spanned<&mut Mutable> { } } -impl Spanned> { +impl Spanned { pub(crate) fn transparent_clone(&self) -> ExecutionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; Ok(value) } } -impl Mutable { +impl AnyValueMutable { pub(crate) fn new_from_owned(value: AnyValue) -> Self { // Unwrap is safe because it's a new refcell Mutable(MutableSubRcRefCell::new(Rc::new(RefCell::new(value))).unwrap()) @@ -375,7 +375,7 @@ impl DerefMut for Mutable { } } -pub(crate) type SharedValue = Shared; +pub(crate) type SharedValue = AnyValueShared; /// A simple wrapper for a shared (immutable) reference to a value. /// @@ -405,7 +405,7 @@ impl Shared { pub(crate) static SHARED_ERROR_MESSAGE: &str = "The variable cannot be read as it is already being modified"; -impl Spanned<&mut Shared> { +impl Spanned<&mut AnyValueShared> { /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -425,14 +425,14 @@ impl Spanned<&mut Shared> { } } -impl Spanned> { +impl Spanned { pub(crate) fn transparent_clone(&self) -> ExecutionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; Ok(value) } } -impl Shared { +impl AnyValueShared { pub(crate) fn new_from_owned(value: AnyValue) -> Self { // Unwrap is safe because it's a new refcell Shared(SharedSubRcRefCell::new(Rc::new(RefCell::new(value))).unwrap()) @@ -605,7 +605,7 @@ impl Deref for CopyOnWrite { impl CopyOnWrite { /// Converts to owned, cloning if necessary - pub(crate) fn clone_to_owned_infallible(self) -> Owned { + pub(crate) fn clone_to_owned_infallible(self) -> AnyValueOwned { match self.inner { CopyOnWriteInner::Owned(owned) => owned, CopyOnWriteInner::SharedWithInfallibleCloning(shared) => shared.infallible_clone(), @@ -617,7 +617,7 @@ impl CopyOnWrite { pub(crate) fn clone_to_owned_transparently( self, span: SpanRange, - ) -> ExecutionResult> { + ) -> ExecutionResult { match self.inner { CopyOnWriteInner::Owned(owned) => Ok(owned), CopyOnWriteInner::SharedWithInfallibleCloning(shared) => Ok(shared.infallible_clone()), From 9b157f28dc0cbcb5ab5b96d03529a5d5084ff4dc Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 10 Jan 2026 19:35:30 +0000 Subject: [PATCH 34/58] tweak: Add LeafLifetimeCapture parameter to handle transmute --- plans/TODO.md | 18 ++++++++++ src/expressions/concepts/form.rs | 36 ++++++++++++++----- src/expressions/concepts/forms/any_mut.rs | 1 + src/expressions/concepts/forms/any_ref.rs | 1 + src/expressions/concepts/forms/argument.rs | 1 + src/expressions/concepts/forms/assignee.rs | 1 + .../concepts/forms/copy_on_write.rs | 18 ++++++++++ src/expressions/concepts/forms/late_bound.rs | 1 + src/expressions/concepts/forms/mutable.rs | 1 + src/expressions/concepts/forms/owned.rs | 1 + .../concepts/forms/referenceable.rs | 1 + src/expressions/concepts/forms/shared.rs | 1 + src/expressions/concepts/forms/simple_mut.rs | 1 + src/expressions/concepts/forms/simple_ref.rs | 1 + .../evaluation/assignment_frames.rs | 6 ++-- 15 files changed, 78 insertions(+), 11 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index c6b279f4..6d86b4fa 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -248,7 +248,25 @@ First, read the @./2025-11-vision.md - [ ] Stage 2 of the form migration: - [x] Attempt to improve mappers: - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. + - [ ] Create macro to define all 15 variants of mapper: + - [ ] Optional state + - [ ] Optional bounds on Form or Type + - [ ] Input: `(self, &self, &mut self)` + - [ ] Output: (`Leaf`, `TryLeaf`) x (`'r` bound / `'static` bound) or `Reduce` + - [ ] ... Can we make this simpler somehow; to reduce the number of implementations on each leaf and improve compile time? + - [ ] Combining lifetimes ==> Tried adding a `<'o>` + - [ ] Combining `self` / `&self` / `&mut self` + - [ ] Combining output kinds? + - [ ] Create some macros to help build `self` / `&self` / `&mut self` methods across all contents + of a form. Use `IsSelfCopyOnWriteContent` as an example to try implementing this + - [ ] Get rid of `CopyOnWrite`, have only CopyOnWriteValue + - [ ] Get rid of `Shared`, `Mutable` and `Assignee`, have only `AnyValueMutable` or other named kinds - [ ] Migrate `Shared`, `Mutable`, `Assignee` + - [ ] `Shared` only works for leaves + - [ ] For specific parents, use e.g. `AnyValueShared` + - [ ] If you need something to apply across all leaves, implement it on some trait + `IsSelfSharedContent` depending/auto-implemented on `IsSelfValueContent
` + - [ ] Same for `Mutable` and `Assignee` - [ ] Stage 3 - [ ] Migrate `CopyOnWrite` and relevant interconversions - [ ] Finish migrating to new Argument/Returned resolution and delete old code diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index b8ec60b0..9f3572a6 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -25,9 +25,29 @@ impl> IsFormOf for F { type Content<'a> = F::KindedContent<'a>; } +trait LeafLifetimeSpecifier {} +#[allow(non_camel_case_types)] +pub(crate) struct UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; +impl LeafLifetimeSpecifier for UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime {} +pub(crate) struct LeafCapturesLifetime; +impl LeafLifetimeSpecifier for LeafCapturesLifetime {} + pub(crate) trait IsHierarchicalForm: IsForm { /// The standard leaf for a hierachical type type Leaf<'a, T: IsValueLeaf>; + + /// Asserts that the form has a 'static leaf type, independent of `'a`. + type LeafLifetimeCapture: LeafLifetimeSpecifier; + + fn leaf_to_static<'a, T: IsValueLeaf>( + leaf: Self::Leaf<'a, T>, + ) -> Self::Leaf<'static, T> + where Self : IsHierarchicalForm + { + // SAFETY: By defining LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime, + // The implementor has asserted that the leaf type does not actually depend on the lifetime 'a. + unsafe { std::mem::transmute::, Self::Leaf<'static, T>>(leaf) } + } } impl IsFormOfForKind @@ -37,19 +57,19 @@ impl IsFormOfForKind(leaf: &'r Self::Leaf<'a, T>) -> &'r T; + fn leaf_as_ref<'r, 'a: 'r, L: IsValueLeaf>(leaf: &'r Self::Leaf<'a, L>) -> &'r L; - fn leaf_clone_to_owned_infallible<'r, 'a: 'r, T: IsValueLeaf>( - leaf: &'r Self::Leaf<'a, T>, - ) -> T { + fn leaf_clone_to_owned_infallible<'r, 'a: 'r, L: IsValueLeaf>( + leaf: &'r Self::Leaf<'a, L>, + ) -> L { Self::leaf_as_ref(leaf).clone() } - fn leaf_clone_to_owned_transparently<'r, 'a: 'r, T: IsValueLeaf>( - leaf: &'r Self::Leaf<'a, T>, + fn leaf_clone_to_owned_transparently<'r, 'a: 'r, L: IsValueLeaf>( + leaf: &'r Self::Leaf<'a, L>, error_span: SpanRange, - ) -> ExecutionResult { - let type_kind = ::Type::type_kind(); + ) -> ExecutionResult { + let type_kind = ::Type::type_kind(); if type_kind.supports_transparent_cloning() { Ok(Self::leaf_clone_to_owned_infallible(leaf)) } else { diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index 32c750de..d1cef211 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -5,6 +5,7 @@ pub(crate) struct BeAnyMut; impl IsForm for BeAnyMut {} impl IsHierarchicalForm for BeAnyMut { type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyMut<'a, T>; + type LeafLifetimeCapture = LeafCapturesLifetime; } impl IsDynCompatibleForm for BeAnyMut { diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 123926d1..65621c68 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -8,6 +8,7 @@ impl IsForm for BeAnyRef {} impl IsHierarchicalForm for BeAnyRef { type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyRef<'a, T>; + type LeafLifetimeCapture = LeafCapturesLifetime; } impl IsDynCompatibleForm for BeAnyRef { diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index b3ce860a..08e9a8c0 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -8,6 +8,7 @@ impl IsForm for BeArgument {} impl IsHierarchicalForm for BeArgument { type Leaf<'a, T: IsValueLeaf> = ArgumentContent; + type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl MapFromArgument for BeArgument { diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 40e0319d..77fd71e9 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -8,6 +8,7 @@ impl IsForm for BeAssignee {} impl IsHierarchicalForm for BeAssignee { type Leaf<'a, T: IsValueLeaf> = QqqAssignee; + type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeAssignee { diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index be580afb..8b759f30 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -8,6 +8,7 @@ impl IsForm for BeCopyOnWrite {} impl IsHierarchicalForm for BeCopyOnWrite { type Leaf<'a, T: IsValueLeaf> = CopyOnWriteContent; + type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeCopyOnWrite { @@ -45,3 +46,20 @@ pub(crate) enum CopyOnWriteContent { /// A transparent clone may fail in this case at use time. SharedWithTransparentCloning(Shared), } + +// pub(crate) trait IsSelfCopyOnWriteContent<'a>: IsSelfValueContent<'a> +// where +// Self: IsValueContent<'a, Form = BeCopyOnWrite>, +// BeCopyOnWrite: IsFormOf<>::Type, Content<'a> = Self>, +// { +// fn acts_as_shared_reference(&self) -> bool { +// struct ThisMapper; +// impl +// self.map_ref_with(mapper) +// } +// } + +// impl<'a, C: IsSelfValueContent<'a>> IsSelfCopyOnWriteContent<'a> for A +// where +// Self: IsValueContent<'a, Form = BeCopyOnWrite>, +// {} \ No newline at end of file diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index a625ebc7..6a7244ee 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -18,6 +18,7 @@ impl IsForm for BeLateBound {} impl IsHierarchicalForm for BeLateBound { type Leaf<'a, T: IsValueLeaf> = LateBoundContent; + type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeLateBound { diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index 909fce22..446bea46 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -8,6 +8,7 @@ impl IsForm for BeMutable {} impl IsHierarchicalForm for BeMutable { type Leaf<'a, T: IsValueLeaf> = QqqMutable; + type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeMutable { diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 340c63f6..3e9c2b2c 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -14,6 +14,7 @@ impl IsForm for BeOwned {} impl IsHierarchicalForm for BeOwned { type Leaf<'a, T: IsValueLeaf> = T; + type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeOwned { diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index 001170ed..c7459b7c 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -13,6 +13,7 @@ impl IsForm for BeReferenceable {} impl IsHierarchicalForm for BeReferenceable { type Leaf<'a, T: IsValueLeaf> = Rc>; + type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl MapFromArgument for BeReferenceable { diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 78b37bf1..97a58ca8 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -8,6 +8,7 @@ impl IsForm for BeShared {} impl IsHierarchicalForm for BeShared { type Leaf<'a, T: IsValueLeaf> = QqqShared; + type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeShared { diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index ed46c4e7..849b5edd 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -11,6 +11,7 @@ impl IsForm for BeMut {} impl IsHierarchicalForm for BeMut { type Leaf<'a, T: IsValueLeaf> = &'a mut T; + type LeafLifetimeCapture = LeafCapturesLifetime; } impl IsDynCompatibleForm for BeMut { diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 0bc1fd02..0f126343 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -11,6 +11,7 @@ impl IsForm for BeRef {} impl IsHierarchicalForm for BeRef { type Leaf<'a, T: IsValueLeaf> = &'a T; + type LeafLifetimeCapture = LeafCapturesLifetime; } impl IsDynCompatibleForm for BeRef { diff --git a/src/expressions/evaluation/assignment_frames.rs b/src/expressions/evaluation/assignment_frames.rs index 933201c8..4d858b5c 100644 --- a/src/expressions/evaluation/assignment_frames.rs +++ b/src/expressions/evaluation/assignment_frames.rs @@ -259,12 +259,12 @@ impl ObjectBasedAssigner { mut self: Box, context: AssignmentContext, access: IndexAccess, - index: &AnyValue, + index: AnyValueRef, assignee_node: ExpressionNodeId, ) -> ExecutionResult { let key: &str = index .spanned(access.span_range()) - .resolve_as("An object key")?; + .downcast_resolve("An object key")?; let value = self.resolve_value(key.to_string(), access.span())?; Ok(context.request_assignment(self, assignee_node, value)) } @@ -324,7 +324,7 @@ impl EvaluationFrame for Box { access, } => { let index_place = value.expect_shared(); - self.handle_index_value(context, access, index_place.as_ref(), assignee_node) + self.handle_index_value(context, access, index_place.as_ref_value(), assignee_node) } ObjectAssignmentState::WaitingForSubassignment => { let AssignmentCompletion = value.expect_assignment_completion(); From 3616d018e8192d0a82070d72856a2fbec71dc02f Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 10 Jan 2026 23:18:39 +0000 Subject: [PATCH 35/58] refactor: Move out mappers to mapping.rs --- plans/TODO.md | 17 +++++- src/expressions/concepts/mapping.rs | 77 +++++++++++++++++++++++++ src/expressions/concepts/mod.rs | 2 + src/expressions/concepts/type_traits.rs | 36 +----------- 4 files changed, 97 insertions(+), 35 deletions(-) create mode 100644 src/expressions/concepts/mapping.rs diff --git a/plans/TODO.md b/plans/TODO.md index 6d86b4fa..8cfada83 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -252,9 +252,22 @@ First, read the @./2025-11-vision.md - [ ] Optional state - [ ] Optional bounds on Form or Type - [ ] Input: `(self, &self, &mut self)` + - [ ] OUTPUT SOLUTION: + - [ ] Introduce on Mapper: `type Output<'a, 'r, FIn, T>: MapperOutput` + - [ ] `trait MapperOutput` has a method `to_parent() where T: IsChildType` + - [ ] Example outputs include: + - [ ] `MapperOutputValue(O)` which is valid for all `T` + - [ ] `MapperOutputContent<'r, F, T>(<'r Content>)` + - [ ] `MapperOutputStaticContent(<'static Content>)` (if needed? Can probably just use `MapperOutputContent<'static, F, T>`) + - [ ] `MapperOutputTryContent<'i, Fin,'o Fout>(Result<'o 'Fout Content, 'i Fin Content>)` - [ ] Output: (`Leaf`, `TryLeaf`) x (`'r` bound / `'static` bound) or `Reduce` - - [ ] ... Can we make this simpler somehow; to reduce the number of implementations on each leaf and improve compile time? - - [ ] Combining lifetimes ==> Tried adding a `<'o>` + - [ ] Content + - [ ] Content + - [ ] Result + - [ ] Result, OldContent>, Infallible> + - [ ] Remove `LeafLifetimeCapture: LeafLifetimeSpecifier` if it's not useful + - [ ] ... Can we make this simpler somehow; to reduce the number of implementations on each type (or at least each leaf) and improve compile time? + - [ ] Combining lifetimes ==> Tried adding a `<'o>` - FAILED. Couldn't find a way to determine an 'o which worked for all `L`, as `for` isn't a thing. - [ ] Combining `self` / `&self` / `&mut self` - [ ] Combining output kinds? - [ ] Create some macros to help build `self` / `&self` / `&mut self` methods across all contents diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs new file mode 100644 index 00000000..47e89e9e --- /dev/null +++ b/src/expressions/concepts/mapping.rs @@ -0,0 +1,77 @@ +use super::*; + +pub(crate) trait LeafMapper { + type OutputForm: IsHierarchicalForm; + type ShortCircuit<'a>; + + fn map_leaf<'a, L: IsValueLeaf>( + self, + leaf: F::Leaf<'a, L>, + ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>>; +} + +pub(crate) trait RefLeafMapper { + type OutputForm: IsHierarchicalForm; + type ShortCircuit<'a>; + + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + self, + leaf: &'r F::Leaf<'a, L>, + ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; +} + +pub(crate) trait MutLeafMapper { + type OutputForm: IsHierarchicalForm; + type ShortCircuit<'a>; + + fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + self, + leaf: &'r mut F::Leaf<'a, L>, + ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; +} + +// pub(crate) trait MutLeafMapper { +// type Output<'r, 'a: 'r, T>: MapperOutput; + +// fn map_leaf<'r, 'a: 'r, T: IsType, L: IsValueLeaf>( +// self, +// leaf: &'r mut F::Leaf<'a, L>, +// ) -> Self::Output<'r, 'a, T>; +// } + +// pub(crate) trait MapperOutput {} + +// pub(crate) trait MapperOutputToParent: MapperOutput { +// type ParentOutput: MapperOutput; + +// fn to_parent_output(self) -> Self::ParentOutput; +// } + +// pub(crate) struct MapperOutputValue(pub(crate) O); + +// impl MapperOutput for MapperOutputValue {} + +// impl MapperOutputToParent

for MapperOutputValue { +// type ParentOutput = Self; + +// fn to_parent_output(self) -> Self::ParentOutput { +// self +// } +// } + +// pub(crate) struct MapperOutputContent<'a, T: IsType, F: IsFormOf>(pub(crate) F::Content<'a>); + +// impl<'a, T: IsType, F: IsFormOf> MapperOutput for MapperOutputContent<'a, T, F> {} + +// impl<'a, P: IsHierarchicalType, C: IsChildType, F: IsHierarchicalForm + IsFormOf + IsFormOf

> +// MapperOutputToParent

for MapperOutputContent<'a, C, F> +// where +// for<'l> C: IsHierarchicalType = >::Content<'l>>, +// for<'l> P: IsHierarchicalType = >::Content<'l>>, +// { +// type ParentOutput = MapperOutputContent<'a, P, F>; + +// fn to_parent_output(self) -> Self::ParentOutput { +// MapperOutputContent::<'a, P, F>(C::into_parent::(self.0)) +// } +// } \ No newline at end of file diff --git a/src/expressions/concepts/mod.rs b/src/expressions/concepts/mod.rs index 7cedeec5..4e1d0177 100644 --- a/src/expressions/concepts/mod.rs +++ b/src/expressions/concepts/mod.rs @@ -4,9 +4,11 @@ use super::*; mod content; mod form; mod forms; +mod mapping; mod type_traits; pub(crate) use content::*; pub(crate) use form::*; pub(crate) use forms::*; +pub(crate) use mapping::*; pub(crate) use type_traits::*; diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 2ca19e77..0d44d0db 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -28,17 +28,17 @@ pub(crate) trait IsHierarchicalType: IsType { fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( mapper: M, - structure: Self::Content<'a, F>, + content: Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( mapper: M, - structure: &'r Self::Content<'a, F>, + content: &'r Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( mapper: M, - structure: &'r mut Self::Content<'a, F>, + content: &'r mut Self::Content<'a, F>, ) -> Result, M::ShortCircuit<'a>>; fn content_to_leaf_kind( @@ -54,36 +54,6 @@ pub(crate) trait IsDynType: IsType { type DynContent: ?Sized + 'static; } -pub(crate) trait LeafMapper { - type OutputForm: IsHierarchicalForm; - type ShortCircuit<'a>; - - fn map_leaf<'a, L: IsValueLeaf>( - self, - leaf: F::Leaf<'a, L>, - ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>>; -} - -pub(crate) trait RefLeafMapper { - type OutputForm: IsHierarchicalForm; - type ShortCircuit<'a>; - - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( - self, - leaf: &'r F::Leaf<'a, L>, - ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; -} - -pub(crate) trait MutLeafMapper { - type OutputForm: IsHierarchicalForm; - type ShortCircuit<'a>; - - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( - self, - leaf: &'r mut F::Leaf<'a, L>, - ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; -} - pub(crate) trait UpcastTo + IsFormOf>: IsType { fn upcast_to<'a>( content: >::Content<'a>, From d31da336bdca0dd538f8b6c6dbb1786ef6ac2411 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 00:58:52 +0000 Subject: [PATCH 36/58] refactor: Partially revert parts --- plans/TODO.md | 22 ++++++++---- src/expressions/concepts/form.rs | 26 ++------------ src/expressions/concepts/forms/any_mut.rs | 1 - src/expressions/concepts/forms/any_ref.rs | 1 - src/expressions/concepts/forms/argument.rs | 1 - src/expressions/concepts/forms/assignee.rs | 1 - .../concepts/forms/copy_on_write.rs | 5 ++- src/expressions/concepts/forms/late_bound.rs | 1 - src/expressions/concepts/forms/mutable.rs | 1 - src/expressions/concepts/forms/owned.rs | 1 - .../concepts/forms/referenceable.rs | 1 - src/expressions/concepts/forms/shared.rs | 1 - src/expressions/concepts/forms/simple_mut.rs | 35 ++++++++++++++++++- src/expressions/concepts/forms/simple_ref.rs | 1 - src/expressions/concepts/mapping.rs | 24 ++++++++++++- 15 files changed, 77 insertions(+), 45 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 8cfada83..e5474ec9 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -252,7 +252,15 @@ First, read the @./2025-11-vision.md - [ ] Optional state - [ ] Optional bounds on Form or Type - [ ] Input: `(self, &self, &mut self)` - - [ ] OUTPUT SOLUTION: + - [ ] Output: (`Leaf`, `TryLeaf`) x (`'r` bound / `'static` bound) or `Reduce` + - [ ] Content + - [ ] Content + - [ ] Result + - [ ] Result, OldContent>, Infallible> + - [ ] Remove `LeafLifetimeCapture: LeafLifetimeSpecifier` if it's not useful + - [ ] ONE POSSIBLE SOLUTION: + - See commented out code in mapping.rs - doesn't work well because + in a parent, it's hard / impossible to come up with the where constraints needed to allow the compiler to find and type check the `to_parent_output` call... and constrains the type to the leaf - [ ] Introduce on Mapper: `type Output<'a, 'r, FIn, T>: MapperOutput` - [ ] `trait MapperOutput` has a method `to_parent() where T: IsChildType` - [ ] Example outputs include: @@ -260,12 +268,12 @@ First, read the @./2025-11-vision.md - [ ] `MapperOutputContent<'r, F, T>(<'r Content>)` - [ ] `MapperOutputStaticContent(<'static Content>)` (if needed? Can probably just use `MapperOutputContent<'static, F, T>`) - [ ] `MapperOutputTryContent<'i, Fin,'o Fout>(Result<'o 'Fout Content, 'i Fin Content>)` - - [ ] Output: (`Leaf`, `TryLeaf`) x (`'r` bound / `'static` bound) or `Reduce` - - [ ] Content - - [ ] Content - - [ ] Result - - [ ] Result, OldContent>, Infallible> - - [ ] Remove `LeafLifetimeCapture: LeafLifetimeSpecifier` if it's not useful + - [ ] SECOND POSSIBLE SOLUTION - no go. + - Just use `upcast()` so that the mapper can return a fixed value. + - The issue is that the leaf mapper works on *any leaf*, but e.g. an `as_mut_value()` might be expected to return an `IntegerValueMutable<'a>`, and I can't express a generic trait across all L such that it works. + - [ ] CURRENT SUGGESTED APPROACH: + - Remove `FormOf` + - Try SOLUTION ONE again - [ ] ... Can we make this simpler somehow; to reduce the number of implementations on each type (or at least each leaf) and improve compile time? - [ ] Combining lifetimes ==> Tried adding a `<'o>` - FAILED. Couldn't find a way to determine an 'o which worked for all `L`, as `for` isn't a thing. - [ ] Combining `self` / `&self` / `&mut self` diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 9f3572a6..7037cdc1 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -25,29 +25,9 @@ impl> IsFormOf for F { type Content<'a> = F::KindedContent<'a>; } -trait LeafLifetimeSpecifier {} -#[allow(non_camel_case_types)] -pub(crate) struct UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; -impl LeafLifetimeSpecifier for UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime {} -pub(crate) struct LeafCapturesLifetime; -impl LeafLifetimeSpecifier for LeafCapturesLifetime {} - pub(crate) trait IsHierarchicalForm: IsForm { /// The standard leaf for a hierachical type type Leaf<'a, T: IsValueLeaf>; - - /// Asserts that the form has a 'static leaf type, independent of `'a`. - type LeafLifetimeCapture: LeafLifetimeSpecifier; - - fn leaf_to_static<'a, T: IsValueLeaf>( - leaf: Self::Leaf<'a, T>, - ) -> Self::Leaf<'static, T> - where Self : IsHierarchicalForm - { - // SAFETY: By defining LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime, - // The implementor has asserted that the leaf type does not actually depend on the lifetime 'a. - unsafe { std::mem::transmute::, Self::Leaf<'static, T>>(leaf) } - } } impl IsFormOfForKind @@ -82,7 +62,7 @@ pub(crate) trait LeafAsRefForm: IsHierarchicalForm { } pub(crate) trait LeafAsMutForm: IsHierarchicalForm { - fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T; + fn leaf_as_mut<'r, 'a: 'r, L: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, L>) -> &'r mut L; } pub(crate) trait IsDynCompatibleForm: IsForm { @@ -97,8 +77,8 @@ impl IsFormOfForKind fo } pub(crate) trait IsDynMappableForm: IsHierarchicalForm + IsDynCompatibleForm { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, L: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, L>, ) -> Option>; } diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index d1cef211..32c750de 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -5,7 +5,6 @@ pub(crate) struct BeAnyMut; impl IsForm for BeAnyMut {} impl IsHierarchicalForm for BeAnyMut { type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyMut<'a, T>; - type LeafLifetimeCapture = LeafCapturesLifetime; } impl IsDynCompatibleForm for BeAnyMut { diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 65621c68..123926d1 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -8,7 +8,6 @@ impl IsForm for BeAnyRef {} impl IsHierarchicalForm for BeAnyRef { type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyRef<'a, T>; - type LeafLifetimeCapture = LeafCapturesLifetime; } impl IsDynCompatibleForm for BeAnyRef { diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index 08e9a8c0..b3ce860a 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -8,7 +8,6 @@ impl IsForm for BeArgument {} impl IsHierarchicalForm for BeArgument { type Leaf<'a, T: IsValueLeaf> = ArgumentContent; - type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl MapFromArgument for BeArgument { diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 77fd71e9..40e0319d 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -8,7 +8,6 @@ impl IsForm for BeAssignee {} impl IsHierarchicalForm for BeAssignee { type Leaf<'a, T: IsValueLeaf> = QqqAssignee; - type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeAssignee { diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 8b759f30..ef129848 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -8,7 +8,6 @@ impl IsForm for BeCopyOnWrite {} impl IsHierarchicalForm for BeCopyOnWrite { type Leaf<'a, T: IsValueLeaf> = CopyOnWriteContent; - type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeCopyOnWrite { @@ -54,7 +53,7 @@ pub(crate) enum CopyOnWriteContent { // { // fn acts_as_shared_reference(&self) -> bool { // struct ThisMapper; -// impl +// impl // self.map_ref_with(mapper) // } // } @@ -62,4 +61,4 @@ pub(crate) enum CopyOnWriteContent { // impl<'a, C: IsSelfValueContent<'a>> IsSelfCopyOnWriteContent<'a> for A // where // Self: IsValueContent<'a, Form = BeCopyOnWrite>, -// {} \ No newline at end of file +// {} diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index 6a7244ee..a625ebc7 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -18,7 +18,6 @@ impl IsForm for BeLateBound {} impl IsHierarchicalForm for BeLateBound { type Leaf<'a, T: IsValueLeaf> = LateBoundContent; - type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeLateBound { diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index 446bea46..909fce22 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -8,7 +8,6 @@ impl IsForm for BeMutable {} impl IsHierarchicalForm for BeMutable { type Leaf<'a, T: IsValueLeaf> = QqqMutable; - type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeMutable { diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 3e9c2b2c..340c63f6 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -14,7 +14,6 @@ impl IsForm for BeOwned {} impl IsHierarchicalForm for BeOwned { type Leaf<'a, T: IsValueLeaf> = T; - type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeOwned { diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index c7459b7c..001170ed 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -13,7 +13,6 @@ impl IsForm for BeReferenceable {} impl IsHierarchicalForm for BeReferenceable { type Leaf<'a, T: IsValueLeaf> = Rc>; - type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl MapFromArgument for BeReferenceable { diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 97a58ca8..78b37bf1 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -8,7 +8,6 @@ impl IsForm for BeShared {} impl IsHierarchicalForm for BeShared { type Leaf<'a, T: IsValueLeaf> = QqqShared; - type LeafLifetimeCapture = UNSAFE_DECLARTION_LeafDoesNotCaptureLifetime; } impl IsDynCompatibleForm for BeShared { diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index 849b5edd..f7e8b551 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -11,7 +11,6 @@ impl IsForm for BeMut {} impl IsHierarchicalForm for BeMut { type Leaf<'a, T: IsValueLeaf> = &'a mut T; - type LeafLifetimeCapture = LeafCapturesLifetime; } impl IsDynCompatibleForm for BeMut { @@ -45,3 +44,37 @@ impl MutLeafMapper for ToMutMapper { Ok(F::leaf_as_mut(leaf)) } } + +// impl> MutLeafMapper for ToMutMapper { +// type Output<'r, 'a: 'r> = AnyValueContent<'r, BeMut>; + +// fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( +// self, +// leaf: &'r mut F::Leaf<'a, L>, +// ) -> Self::Output<'r, 'a> +// where +// for<'x> &'x mut L: IsValueContent<'x, Form = BeMut>, +// for<'x> <&'x mut L as IsValueContent<'x>>::Type: UpcastTo, +// BeMut: for<'x> IsFormOf<<&'x mut L as IsValueContent<'x>>::Type, Content<'r> = &'r mut L>, +// { +// let my_mut = F::leaf_as_mut(leaf); +// <<&'r mut L as IsValueContent<'r>>::Type as UpcastTo>::upcast_to(my_mut) +// } +// } + +// impl> MutLeafMapper for ToMutMapper { +// type Output<'r, 'a: 'r> = AnyValueContent<'r, BeMut>; + +// fn map_leaf<'r, 'a: 'r, T: IsType, L: IsValueLeaf>( +// self, +// leaf: &'r mut F::Leaf<'a, L>, +// ) -> Self::Output<'r, 'a> +// where +// for<'x> &'x mut L: IsValueContent<'x, Form = BeMut, Type = T>, +// T: UpcastTo, +// BeMut: IsFormOf = &'r mut L>, +// { +// let my_mut = F::leaf_as_mut(leaf); +// <<&'r mut L as IsValueContent<'r>>::Type as UpcastTo>::upcast_to(my_mut) +// } +// } diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 0f126343..0bc1fd02 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -11,7 +11,6 @@ impl IsForm for BeRef {} impl IsHierarchicalForm for BeRef { type Leaf<'a, T: IsValueLeaf> = &'a T; - type LeafLifetimeCapture = LeafCapturesLifetime; } impl IsDynCompatibleForm for BeRef { diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index 47e89e9e..cc8c06f9 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -30,6 +30,28 @@ pub(crate) trait MutLeafMapper { ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; } +// pub(crate) trait MutLeafMapper { +// type Output<'r, 'a: 'r>; + +// fn map_leaf<'r, 'a: 'r, T: IsType, L: IsValueLeaf>( +// self, +// leaf: &'r mut F::Leaf<'a, L>, +// ) -> Self::Output<'r, 'a> +// where +// for<'x> &'x mut L: IsValueContent<'x, Form = BeMut, Type = T>, +// T: UpcastTo, +// BeMut: IsFormOf = &'r mut L>, +// ; +// } + +// impl IsMutValueLeaf for L +// where +// for<'x> &'x mut L: IsValueContent<'x, Form = BeMut>, +// for<'x> <&'x mut L as IsValueContent<'x>>::Type: UpcastTo, +// { +// type MutType<'a> = <&'a mut L as IsValueContent<'a>>::Type; +// } + // pub(crate) trait MutLeafMapper { // type Output<'r, 'a: 'r, T>: MapperOutput; @@ -74,4 +96,4 @@ pub(crate) trait MutLeafMapper { // fn to_parent_output(self) -> Self::ParentOutput { // MapperOutputContent::<'a, P, F>(C::into_parent::(self.0)) // } -// } \ No newline at end of file +// } From 33881f8209f40b3fb2ff57384374d3f9ca2c3cd8 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 01:08:01 +0000 Subject: [PATCH 37/58] fix: Fix benchmark compilation --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6b24f889..aa43efe6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -584,7 +584,7 @@ mod benchmarking { let entry_span = Span::call_site().span_range(); let returned_stream = parsed .evaluate_spanned(&mut interpreter, entry_span, RequestedOwnership::owned()) - .and_then(|x| x.expect_owned().map(|owned| owned.0).into_stream()) + .and_then(|x| x.expect_owned().into_stream()) .convert_to_final_result()?; let mut output_stream = interpreter.complete(); From c630687059083afe8ac0e9a77dc253c657f0b116 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 01:49:08 +0000 Subject: [PATCH 38/58] refactor: Assume hierarchical types in more places --- src/expressions/concepts/content.rs | 122 +++------ src/expressions/concepts/form.rs | 36 +-- src/expressions/concepts/forms/any_mut.rs | 2 +- src/expressions/concepts/forms/any_ref.rs | 4 +- src/expressions/concepts/forms/argument.rs | 4 +- src/expressions/concepts/forms/assignee.rs | 2 +- .../concepts/forms/copy_on_write.rs | 4 +- src/expressions/concepts/forms/late_bound.rs | 2 +- src/expressions/concepts/forms/mutable.rs | 2 +- src/expressions/concepts/forms/owned.rs | 2 +- .../concepts/forms/referenceable.rs | 4 +- src/expressions/concepts/forms/shared.rs | 2 +- src/expressions/concepts/forms/simple_mut.rs | 2 +- src/expressions/concepts/forms/simple_ref.rs | 2 +- src/expressions/concepts/type_traits.rs | 239 +++++------------- 15 files changed, 112 insertions(+), 317 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index af983c39..1912c473 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -3,50 +3,46 @@ use std::mem::transmute; use super::*; /// Shorthand for representing the form F of a type T with a particular lifetime 'a. -pub(crate) type Actual<'a, T, F> = >::Content<'a>; +pub(crate) type Content<'a, T, F> = ::Content<'a, F>; +pub(crate) type DynContent<'a, D, F> = + ::DynLeaf<'a, ::DynContent>; /// For types which have an associated value (type and form) pub(crate) trait IsValueContent<'a> { - type Type: IsType; - type Form: IsFormOf; + type Type: IsHierarchicalType; + type Form: IsHierarchicalForm; } pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { - fn from_content(content: >::Content<'a>) -> Self; + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self; } -pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { - fn into_content(self) -> >::Content<'a>; +pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> +where + Self: Sized, +{ + fn into_content(self) -> Content<'a, Self::Type, Self::Form>; #[inline] - fn upcast(self) -> Actual<'a, S, Self::Form> + fn upcast(self) -> Content<'a, S, Self::Form> where - Self: Sized, - Self::Form: IsFormOf, Self::Type: UpcastTo, { >::upcast_to(self.into_content()) } #[inline] - fn downcast>(self) -> Option> + fn downcast(self) -> Option> where - Self: Sized, - Self::Form: IsFormOf, - for<'l> Self::Type: IsHierarchicalType< - Content<'l, Self::Form> = >::Content<'l>, - >, - Self::Form: IsHierarchicalForm, + U: DowncastFrom, { U::downcast_from(self.into_content()) } #[inline] - fn into_any(self) -> Actual<'a, AnyType, Self::Form> + fn into_any(self) -> Content<'a, AnyType, Self::Form> where - Self: Sized, Self::Type: UpcastTo, - Self::Form: IsFormOf, { self.upcast() } @@ -54,24 +50,12 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { fn map_with>( self, mapper: M, - ) -> Result, M::ShortCircuit<'a>> - where - Self: Sized, - Self::Type: IsHierarchicalType< - Content<'a, Self::Form> = >::Content<'a>, - >, - Self::Form: IsHierarchicalForm, - { + ) -> Result, M::ShortCircuit<'a>> { ::map_with::(mapper, self.into_content()) } - fn into_referenceable(self) -> Actual<'a, Self::Type, BeReferenceable> + fn into_referenceable(self) -> Content<'a, Self::Type, BeReferenceable> where - Self: Sized, - Self::Type: IsHierarchicalType< - Content<'a, Self::Form> = >::Content<'a>, - >, - Self::Form: IsHierarchicalForm, for<'l> OwnedToReferenceableMapper: LeafMapper = Infallible>, { @@ -85,8 +69,7 @@ pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> { impl<'a, C> Spanned where C: IntoValueContent<'a>, - for<'l> C::Type: - IsHierarchicalType = >::Content<'l>>, + C::Type: IsHierarchicalType, C::Form: IsHierarchicalForm, { pub(crate) fn downcast_resolve>( @@ -94,7 +77,6 @@ where description: &str, ) -> ExecutionResult where - C::Form: IsFormOf<>::Type>, >::Type: DowncastFrom, { let Spanned(value, span_range) = self; @@ -109,7 +91,6 @@ where description: &str, ) -> ExecutionResult> where - C::Form: IsFormOf<>::Type>, >::Type: DowncastFrom, { let span_range = self.1; @@ -124,7 +105,6 @@ where impl> IntoAnyValue for X where X::Type: UpcastTo, - BeOwned: IsFormOf, { fn into_any_value(self) -> AnyValue { self.into_any() @@ -136,18 +116,15 @@ where /// implement on IntoValueContent / FromValueContent. pub(crate) trait IsSelfValueContent<'a>: IsValueContent<'a> where - Self::Form: IsFormOf = Self>, + Self::Type: IsHierarchicalType = Self>, + Self::Form: IsHierarchicalForm, { fn map_mut_with<'r, M: MutLeafMapper>( &'r mut self, mapper: M, - ) -> Result, M::ShortCircuit<'a>> + ) -> Result, M::ShortCircuit<'a>> where 'a: 'r, - Self::Type: IsHierarchicalType< - Content<'a, Self::Form> = >::Content<'a>, - >, - Self::Form: IsHierarchicalForm, { ::map_mut_with::(mapper, self) } @@ -155,29 +132,17 @@ where fn map_ref_with<'r, M: RefLeafMapper>( &'r self, mapper: M, - ) -> Result, M::ShortCircuit<'a>> + ) -> Result, M::ShortCircuit<'a>> where 'a: 'r, - Self::Type: IsHierarchicalType< - Content<'a, Self::Form> = >::Content<'a>, - >, - Self::Form: IsHierarchicalForm, { ::map_ref_with::(mapper, self) } - fn as_mut_value<'r>(&'r mut self) -> Actual<'r, Self::Type, BeMut> + fn as_mut_value<'r>(&'r mut self) -> Content<'r, Self::Type, BeMut> where - // Bounds for map_mut_with to work 'a: 'r, - Self::Type: IsHierarchicalType< - Content<'a, Self::Form> = >::Content<'a>, - >, - Self::Form: IsHierarchicalForm, - - // Bounds for ToMutMapper to work Self::Form: LeafAsMutForm, - BeMut: IsFormOf, { match self.map_mut_with(ToMutMapper) { Ok(x) => x, @@ -185,16 +150,9 @@ where } } - fn as_ref_value<'r>(&'r self) -> Actual<'r, Self::Type, BeRef> + fn as_ref_value<'r>(&'r self) -> Content<'r, Self::Type, BeRef> where - // Bounds for map_ref_with to work 'a: 'r, - Self::Type: IsHierarchicalType< - Content<'a, Self::Form> = >::Content<'a>, - >, - Self::Form: IsHierarchicalForm, - - // Bounds for ToRefMapper to work Self::Form: LeafAsRefForm, { match self.map_ref_with(ToRefMapper) { @@ -206,19 +164,10 @@ where /// This method should only be used when you are certain that the value should be cloned. /// In most situations, you may wish to use [IsSelfValueContent::clone_to_owned_transparently] /// instead. - fn clone_to_owned_infallible<'r>(&'r self) -> Actual<'static, Self::Type, BeOwned> + fn clone_to_owned_infallible<'r>(&'r self) -> Content<'static, Self::Type, BeOwned> where - // Bounds for map_mut_with to work 'a: 'r, - Self::Type: IsHierarchicalType< - Content<'a, Self::Form> = >::Content<'a>, - >, - Self::Form: IsHierarchicalForm, - - // Bounds for LeafAsRefForm to work Self::Form: LeafAsRefForm, - - // Bounds for cloning to work Self: Sized, { let mapped = match self.map_ref_with(ToOwnedInfallibleMapper) { @@ -230,7 +179,7 @@ where // I'd have liked to make this a where bound, but type resolution gets stuck in // an infinite loop in that case. unsafe { - transmute::, Actual<'static, Self::Type, BeOwned>>( + transmute::, Content<'static, Self::Type, BeOwned>>( mapped, ) } @@ -244,21 +193,11 @@ where fn clone_to_owned_transparently<'r>( &'r self, span_range: SpanRange, - ) -> ExecutionResult> + ) -> ExecutionResult> where - // Bounds for map_mut_with to work 'a: 'r, - Self::Type: IsHierarchicalType< - Content<'a, Self::Form> = >::Content<'a>, - >, - Self::Form: IsHierarchicalForm, - - // Bounds for LeafAsRefForm to work Self::Form: LeafAsRefForm, - - // Bounds for cloning to work Self: Sized, - BeOwned: for<'l> IsFormOf = Self>, { let mapped = self.map_ref_with(ToOwnedTransparentlyMapper { span_range }); // SAFETY: All owned values don't make use of the lifetime parameter, @@ -269,8 +208,8 @@ where #[allow(clippy::useless_transmute)] // Clippy thinks these types are identical but is wrong here transmute::< - ExecutionResult>, - ExecutionResult>, + ExecutionResult>, + ExecutionResult>, >(mapped) } } @@ -279,7 +218,8 @@ where impl<'a, X> IsSelfValueContent<'a> for X where X: IsValueContent<'a>, - X::Form: IsFormOf = X>, + X::Type: IsHierarchicalType = X>, + X::Form: IsHierarchicalForm, { } diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 7037cdc1..8f7f7683 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -13,29 +13,11 @@ use super::*; /// generics. pub(crate) trait IsForm: Sized + Clone {} -pub(crate) trait IsFormOf: IsForm { - type Content<'a>; -} - -pub(crate) trait IsFormOfForKind: IsForm { - type KindedContent<'a>; -} - -impl> IsFormOf for F { - type Content<'a> = F::KindedContent<'a>; -} - pub(crate) trait IsHierarchicalForm: IsForm { /// The standard leaf for a hierachical type type Leaf<'a, T: IsValueLeaf>; } -impl IsFormOfForKind - for F -{ - type KindedContent<'a> = T::Content<'a, F>; -} - pub(crate) trait LeafAsRefForm: IsHierarchicalForm { fn leaf_as_ref<'r, 'a: 'r, L: IsValueLeaf>(leaf: &'r Self::Leaf<'a, L>) -> &'r L; @@ -72,24 +54,22 @@ pub(crate) trait IsDynCompatibleForm: IsForm { type DynLeaf<'a, D: 'static + ?Sized>; } -impl IsFormOfForKind for F { - type KindedContent<'a> = F::DynLeaf<'a, T::DynContent>; -} - pub(crate) trait IsDynMappableForm: IsHierarchicalForm + IsDynCompatibleForm { fn leaf_to_dyn<'a, L: IsValueLeaf + CastDyn, D: ?Sized + 'static>( leaf: Self::Leaf<'a, L>, ) -> Option>; } -pub(crate) trait MapFromArgument: IsFormOf { +pub(crate) trait MapFromArgument: IsHierarchicalForm { const ARGUMENT_OWNERSHIP: ArgumentOwnership; - fn from_argument_value(value: ArgumentValue) - -> ExecutionResult>; + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult>; } -pub(crate) trait MapIntoReturned: IsFormOf { - fn into_returned_value(value: Actual<'static, AnyType, Self>) - -> ExecutionResult; +pub(crate) trait MapIntoReturned: IsHierarchicalForm { + fn into_returned_value( + value: Content<'static, AnyType, Self>, + ) -> ExecutionResult; } diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index 32c750de..04f42ff5 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -36,7 +36,7 @@ impl MapFromArgument for BeAnyMut { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // value.expect_mutable().as_any_mut() } diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 123926d1..81f19109 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -1,6 +1,6 @@ use super::*; -type QqqAnyRef<'a, T> = Actual<'a, T, BeAnyRef>; +type QqqAnyRef<'a, T> = Content<'a, T, BeAnyRef>; #[derive(Copy, Clone)] pub(crate) struct BeAnyRef; @@ -33,7 +33,7 @@ impl MapFromArgument for BeAnyRef { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // value.expect_shared().as_any_ref() } diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index b3ce860a..3553bc3d 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -1,6 +1,6 @@ use super::*; -pub(crate) type QqqArgumentValue = Actual<'static, T, BeArgument>; +pub(crate) type QqqArgumentValue = Content<'static, T, BeArgument>; #[derive(Copy, Clone)] pub(crate) struct BeArgument; @@ -15,7 +15,7 @@ impl MapFromArgument for BeArgument { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // Ok(value) } diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 40e0319d..6431b37a 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -40,7 +40,7 @@ impl MapFromArgument for BeAssignee { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // value.expect_assignee() } diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index ef129848..572bf4fd 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -1,6 +1,6 @@ use super::*; -pub(crate) type QqqCopyOnWrite = Actual<'static, T, BeCopyOnWrite>; +pub(crate) type QqqCopyOnWrite = Content<'static, T, BeCopyOnWrite>; #[derive(Copy, Clone)] pub(crate) struct BeCopyOnWrite; @@ -28,7 +28,7 @@ impl MapFromArgument for BeCopyOnWrite { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { todo!() // value.expect_copy_on_write() } diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index a625ebc7..9ae72f2f 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -10,7 +10,7 @@ use super::*; /// whether the method needs `x[a]` to be a shared reference, mutable reference or an owned value. /// /// So instead, we take the most powerful access we can have for `x[a]`, and convert it later. -pub(crate) type QqqLateBound = Actual<'static, T, BeLateBound>; +pub(crate) type QqqLateBound = Content<'static, T, BeLateBound>; #[derive(Copy, Clone)] pub(crate) struct BeLateBound; diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index 909fce22..492e9bd3 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -39,7 +39,7 @@ impl MapFromArgument for BeMutable { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { // value.expect_mutable() todo!() } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 340c63f6..51af0776 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -45,7 +45,7 @@ impl MapFromArgument for BeOwned { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { Ok(value.expect_owned()) } } diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index 001170ed..61668b51 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -1,6 +1,6 @@ use super::*; -type QqqReferenceable = Actual<'static, T, BeReferenceable>; +type QqqReferenceable = Content<'static, T, BeReferenceable>; /// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. /// This is useful for the content of variables. @@ -20,7 +20,7 @@ impl MapFromArgument for BeReferenceable { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { // Rc::new(RefCell::new(value.expect_owned())) todo!() } diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 78b37bf1..1a2ddf6a 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -33,7 +33,7 @@ impl MapFromArgument for BeShared { fn from_argument_value( _value: ArgumentValue, - ) -> ExecutionResult> { + ) -> ExecutionResult> { // value.expect_shared() todo!() } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index f7e8b551..7bfa16cb 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -1,6 +1,6 @@ use super::*; -pub(crate) type QqqMut<'a, T> = Actual<'a, T, BeMut>; +pub(crate) type QqqMut<'a, T> = Content<'a, T, BeMut>; /// It can't be an argument because arguments must be owned in some way; /// so that the drop glue can work properly (because they may come from diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 0bc1fd02..d17a8dbf 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -1,6 +1,6 @@ use super::*; -pub(crate) type QqqRef<'a, T> = Actual<'a, T, BeRef>; +pub(crate) type QqqRef<'a, T> = Content<'a, T, BeRef>; /// It can't be an argument because arguments must be owned in some way; /// so that the drop glue can work properly (because they may come from diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 0d44d0db..4a673b89 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -19,10 +19,6 @@ pub(crate) trait IsType: Sized { } pub(crate) trait IsHierarchicalType: IsType { - // The following is always true, courtesy of the definition of IsFormOf: - // >::Content<'a>> := Self::Content<'a, F> - // So the following where clause can be added where needed to make types line up: - // for<'l> T: IsHierarchicalType = >::Content<'l>>, type Content<'a, F: IsHierarchicalForm>; type LeafKind: IsLeafKind; @@ -54,27 +50,48 @@ pub(crate) trait IsDynType: IsType { type DynContent: ?Sized + 'static; } -pub(crate) trait UpcastTo + IsFormOf>: IsType { - fn upcast_to<'a>( - content: >::Content<'a>, - ) -> >::Content<'a>; +pub(crate) trait UpcastTo: + IsHierarchicalType +{ + fn upcast_to<'a>(content: Content<'a, Self, F>) -> Content<'a, T, F>; +} + +pub(crate) trait DowncastFrom: + IsHierarchicalType +{ + fn downcast_from<'a>(content: Content<'a, T, F>) -> Option>; + + fn resolve<'a>( + content: Content<'a, T, F>, + span_range: SpanRange, + resolution_target: &str, + ) -> ExecutionResult> { + let leaf_kind = T::content_to_leaf_kind::(&content); + let content = match Self::downcast_from(content) { + Some(c) => c, + None => { + return span_range.value_err(format!( + "{} is expected to be {}, but it is {}", + resolution_target, + Self::ARTICLED_DISPLAY_NAME, + leaf_kind.articled_display_name(), + )) + } + }; + Ok(content) + } } -pub(crate) trait DowncastFrom< - T: IsHierarchicalType, - F: IsHierarchicalForm + IsFormOf + IsFormOf, ->: IsType where - for<'l> T: IsHierarchicalType = >::Content<'l>>, +pub(crate) trait DynResolveFrom: + IsDynType { - fn downcast_from<'a>( - content: >::Content<'a>, - ) -> Option<>::Content<'a>>; + fn downcast_from<'a>(content: Content<'a, T, F>) -> Option>; fn resolve<'a>( - content: >::Content<'a>, + content: Content<'a, T, F>, span_range: SpanRange, resolution_target: &str, - ) -> ExecutionResult<>::Content<'a>> { + ) -> ExecutionResult> { let leaf_kind = T::content_to_leaf_kind::(&content); let content = match Self::downcast_from(content) { Some(c) => c, @@ -154,8 +171,8 @@ macro_rules! impl_ancestor_chain_conversions { impl DowncastFrom<$child, F> for $child { fn downcast_from<'a>( - content: >::Content<'a>, - ) -> Option<>::Content<'a>> { + content: Content<'a, Self, F>, + ) -> Option> { Some(content) } } @@ -163,8 +180,8 @@ macro_rules! impl_ancestor_chain_conversions { impl UpcastTo<$child, F> for $child { fn upcast_to<'a>( - content: >::Content<'a>, - ) -> >::Content<'a> { + content: Content<'a, Self, F>, + ) -> Content<'a, Self, F> { content } } @@ -192,8 +209,8 @@ macro_rules! impl_ancestor_chain_conversions { impl DowncastFrom<$parent, F> for $child { fn downcast_from<'a>( - content: >::Content<'a>, - ) -> Option<>::Content<'a>> { + content: Content<'a, $parent, F>, + ) -> Option> { <$child as IsChildType>::from_parent(content) } } @@ -201,8 +218,8 @@ macro_rules! impl_ancestor_chain_conversions { impl UpcastTo<$parent, F> for $child { fn upcast_to<'a>( - content: >::Content<'a>, - ) -> >::Content<'a> { + content: Content<'a, $child, F>, + ) -> Content<'a, $parent, F> { <$child as IsChildType>::into_parent(content) } } @@ -210,16 +227,16 @@ macro_rules! impl_ancestor_chain_conversions { $( impl DowncastFrom<$ancestor, F> for $child { fn downcast_from<'a>( - content: >::Content<'a>, - ) -> Option<>::Content<'a>> { + content: Content<'a, $ancestor, F>, + ) -> Option> { <$child as DowncastFrom<$parent, F>>::downcast_from(<$parent as DowncastFrom<$ancestor, F>>::downcast_from(content)?) } } impl UpcastTo<$ancestor, F> for $child { fn upcast_to<'a>( - content: >::Content<'a>, - ) -> >::Content<'a> { + content: Content<'a, $child, F>, + ) -> Content<'a, $ancestor, F> { <$parent as UpcastTo<$ancestor, F>>::upcast_to(<$child as UpcastTo<$parent, F>>::upcast_to(content)) } } @@ -235,11 +252,11 @@ pub(crate) trait IsValueLeaf: { } -pub(crate) trait IsDynLeaf: 'static + IsValueContent<'static> +pub(crate) trait IsDynLeaf: 'static where DynMapper: LeafMapper, - Self::Type: IsDynType, { + type Type: IsDynType; } pub(crate) trait CastDyn { @@ -370,12 +387,12 @@ macro_rules! define_parent_type { } $content_vis enum $content<'a, F: IsHierarchicalForm> { - $( $variant(>::Content<'a>), )* + $( $variant(Content<'a, $variant_type, F>), )* } impl<'a, F: IsHierarchicalForm> Clone for $content<'a, F> where - $( >::Content<'a>: Clone ),* + $( Content<'a, $variant_type, F>: Clone ),* { fn clone(&self) -> Self { match self { @@ -387,7 +404,7 @@ macro_rules! define_parent_type { impl<'a, F: IsHierarchicalForm> Copy for $content<'a, F> where - $( >::Content<'a>: Copy ),* + $( Content<'a, $variant_type, F>: Copy ),* {} impl_value_content_traits!(parent: $type_def, $content); @@ -798,145 +815,6 @@ macro_rules! impl_value_content_traits { } } }; - - // For dyn types - implements for all dyn-compatible form wrappers - (dyn: $type_def:ty, $dyn_type:ty) => { - // The unsized dyn type itself needs IsValueContent for IsDynLeaf requirements - impl<'a> IsValueContent<'a> for $dyn_type { - type Type = $type_def; - type Form = BeOwned; - } - - // BeOwned: content is Box - impl<'a> IsValueContent<'a> for Box<$dyn_type> { - type Type = $type_def; - type Form = BeOwned; - } - impl<'a> IntoValueContent<'a> for Box<$dyn_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for Box<$dyn_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeRef: content is &'a D - impl<'a> IsValueContent<'a> for &'a $dyn_type { - type Type = $type_def; - type Form = BeRef; - } - impl<'a> IntoValueContent<'a> for &'a $dyn_type { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for &'a $dyn_type { - fn from_content(content: Self) -> Self { - content - } - } - - // BeMut: content is &'a mut D - impl<'a> IsValueContent<'a> for &'a mut $dyn_type { - type Type = $type_def; - type Form = BeMut; - } - impl<'a> IntoValueContent<'a> for &'a mut $dyn_type { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for &'a mut $dyn_type { - fn from_content(content: Self) -> Self { - content - } - } - - // BeMutable: content is QqqMutable - impl<'a> IsValueContent<'a> for QqqMutable<$dyn_type> { - type Type = $type_def; - type Form = BeMutable; - } - impl<'a> IntoValueContent<'a> for QqqMutable<$dyn_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for QqqMutable<$dyn_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeShared: content is QqqShared - impl<'a> IsValueContent<'a> for QqqShared<$dyn_type> { - type Type = $type_def; - type Form = BeShared; - } - impl<'a> IntoValueContent<'a> for QqqShared<$dyn_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for QqqShared<$dyn_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeAnyRef: content is AnyRef<'a, D> - impl<'a> IsValueContent<'a> for AnyRef<'a, $dyn_type> { - type Type = $type_def; - type Form = BeAnyRef; - } - impl<'a> IntoValueContent<'a> for AnyRef<'a, $dyn_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for AnyRef<'a, $dyn_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeAnyMut: content is AnyMut<'a, D> - impl<'a> IsValueContent<'a> for AnyMut<'a, $dyn_type> { - type Type = $type_def; - type Form = BeAnyMut; - } - impl<'a> IntoValueContent<'a> for AnyMut<'a, $dyn_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for AnyMut<'a, $dyn_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeAssignee: content is QqqAssignee - impl<'a> IsValueContent<'a> for QqqAssignee<$dyn_type> { - type Type = $type_def; - type Form = BeAssignee; - } - impl<'a> IntoValueContent<'a> for QqqAssignee<$dyn_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for QqqAssignee<$dyn_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // Note: BeReferenceable (Rc>) doesn't work for unsized D. - }; } pub(crate) use impl_value_content_traits; @@ -983,16 +861,13 @@ macro_rules! define_dyn_type { impl TypeFeatureResolver for $type_def: [$type_def] } - impl_value_content_traits!(dyn: $type_def, $dyn_type); - - impl IsDynLeaf for $dyn_type {} + impl IsDynLeaf for $dyn_type { + type Type = $type_def; + } - impl + IsFormOf<$type_def> + IsDynMappableForm> DowncastFrom for $type_def - where - for<'a> T: IsHierarchicalType = >::Content<'a>>, - for<'a> F: IsDynCompatibleForm = >::Content<'a>>, + impl DynResolveFrom for $type_def { - fn downcast_from<'a>(content: >::Content<'a>) -> Option<>::Content<'a>> { + fn downcast_from<'a>(content: Content<'a, T, F>) -> Option> { match T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content) { Ok(_) => panic!("DynMapper is expected to always short-circuit"), Err(dyn_leaf) => dyn_leaf, From a3ee6a4d349f1036f88c390d523ab1dc9bd36f5f Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 04:01:41 +0000 Subject: [PATCH 39/58] refactor: Simplify ref and mut mappers --- plans/TODO.md | 38 +---- src/expressions/concepts/content.rs | 49 ++---- src/expressions/concepts/forms/owned.rs | 36 +++-- src/expressions/concepts/forms/simple_mut.rs | 51 ++---- src/expressions/concepts/forms/simple_ref.rs | 17 +- src/expressions/concepts/mapping.rs | 158 ++++++++++--------- src/expressions/concepts/type_traits.rs | 46 ++++-- 7 files changed, 176 insertions(+), 219 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index e5474ec9..0907087f 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -248,38 +248,12 @@ First, read the @./2025-11-vision.md - [ ] Stage 2 of the form migration: - [x] Attempt to improve mappers: - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. - - [ ] Create macro to define all 15 variants of mapper: - - [ ] Optional state - - [ ] Optional bounds on Form or Type - - [ ] Input: `(self, &self, &mut self)` - - [ ] Output: (`Leaf`, `TryLeaf`) x (`'r` bound / `'static` bound) or `Reduce` - - [ ] Content - - [ ] Content - - [ ] Result - - [ ] Result, OldContent>, Infallible> - - [ ] Remove `LeafLifetimeCapture: LeafLifetimeSpecifier` if it's not useful - - [ ] ONE POSSIBLE SOLUTION: - - See commented out code in mapping.rs - doesn't work well because - in a parent, it's hard / impossible to come up with the where constraints needed to allow the compiler to find and type check the `to_parent_output` call... and constrains the type to the leaf - - [ ] Introduce on Mapper: `type Output<'a, 'r, FIn, T>: MapperOutput` - - [ ] `trait MapperOutput` has a method `to_parent() where T: IsChildType` - - [ ] Example outputs include: - - [ ] `MapperOutputValue(O)` which is valid for all `T` - - [ ] `MapperOutputContent<'r, F, T>(<'r Content>)` - - [ ] `MapperOutputStaticContent(<'static Content>)` (if needed? Can probably just use `MapperOutputContent<'static, F, T>`) - - [ ] `MapperOutputTryContent<'i, Fin,'o Fout>(Result<'o 'Fout Content, 'i Fin Content>)` - - [ ] SECOND POSSIBLE SOLUTION - no go. - - Just use `upcast()` so that the mapper can return a fixed value. - - The issue is that the leaf mapper works on *any leaf*, but e.g. an `as_mut_value()` might be expected to return an `IntegerValueMutable<'a>`, and I can't express a generic trait across all L such that it works. - - [ ] CURRENT SUGGESTED APPROACH: - - Remove `FormOf` - - Try SOLUTION ONE again - - [ ] ... Can we make this simpler somehow; to reduce the number of implementations on each type (or at least each leaf) and improve compile time? - - [ ] Combining lifetimes ==> Tried adding a `<'o>` - FAILED. Couldn't find a way to determine an 'o which worked for all `L`, as `for` isn't a thing. - - [ ] Combining `self` / `&self` / `&mut self` - - [ ] Combining output kinds? - - [ ] Create some macros to help build `self` / `&self` / `&mut self` methods across all contents - of a form. Use `IsSelfCopyOnWriteContent` as an example to try implementing this + - [ ] Finish mapper improvements + - [ ] Migrate `self` + - [ ] Consider if we even need `MapperOutput` or just some helper functions + - [ ] Add a `Result` variant which will allow us to delay resolving the type kind into the error case when downcasting + - [ ] Create macro to define inline mappers in various forms + - [ ] Create some macros to help build `self` / `&self` / `&mut self` methods across all contents of a form. Use `IsSelfCopyOnWriteContent` as an example to try implementing this - [ ] Get rid of `CopyOnWrite`, have only CopyOnWriteValue - [ ] Get rid of `Shared`, `Mutable` and `Assignee`, have only `AnyValueMutable` or other named kinds - [ ] Migrate `Shared`, `Mutable`, `Assignee` diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 1912c473..a5b80a57 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -1,5 +1,3 @@ -use std::mem::transmute; - use super::*; /// Shorthand for representing the form F of a type T with a particular lifetime 'a. @@ -122,21 +120,21 @@ where fn map_mut_with<'r, M: MutLeafMapper>( &'r mut self, mapper: M, - ) -> Result, M::ShortCircuit<'a>> + ) -> M::Output<'r, 'a, Self::Type> where 'a: 'r, { - ::map_mut_with::(mapper, self) + ::map_mut_with::(mapper, self) } fn map_ref_with<'r, M: RefLeafMapper>( &'r self, mapper: M, - ) -> Result, M::ShortCircuit<'a>> + ) -> M::Output<'r, 'a, Self::Type> where 'a: 'r, { - ::map_ref_with::(mapper, self) + ::map_ref_with::(mapper, self) } fn as_mut_value<'r>(&'r mut self) -> Content<'r, Self::Type, BeMut> @@ -144,10 +142,7 @@ where 'a: 'r, Self::Form: LeafAsMutForm, { - match self.map_mut_with(ToMutMapper) { - Ok(x) => x, - Err(infallible) => match infallible {}, // Need to include because of MSRV - } + self.map_mut_with(ToMutMapper).0 } fn as_ref_value<'r>(&'r self) -> Content<'r, Self::Type, BeRef> @@ -155,10 +150,7 @@ where 'a: 'r, Self::Form: LeafAsRefForm, { - match self.map_ref_with(ToRefMapper) { - Ok(x) => x, - Err(infallible) => match infallible {}, // Need to include because of MSRV - } + self.map_ref_with(ToRefMapper).0 } /// This method should only be used when you are certain that the value should be cloned. @@ -170,19 +162,7 @@ where Self::Form: LeafAsRefForm, Self: Sized, { - let mapped = match self.map_ref_with(ToOwnedInfallibleMapper) { - Ok(x) => x, - Err(infallible) => match infallible {}, // Need to include because of MSRV - }; - // SAFETY: All owned values don't make use of the lifetime parameter, - // so we can safely transmute to 'static here. - // I'd have liked to make this a where bound, but type resolution gets stuck in - // an infinite loop in that case. - unsafe { - transmute::, Content<'static, Self::Type, BeOwned>>( - mapped, - ) - } + self.map_ref_with(ToOwnedInfallibleMapper).0 } /// A transparent clone is allowed for some types when doing method resolution. @@ -199,19 +179,8 @@ where Self::Form: LeafAsRefForm, Self: Sized, { - let mapped = self.map_ref_with(ToOwnedTransparentlyMapper { span_range }); - // SAFETY: All owned values don't make use of the lifetime parameter, - // so we can safely transmute to 'static here. - // I'd have liked to make this a where bound, but type resolution gets stuck in - // an infinite loop in that case. - unsafe { - #[allow(clippy::useless_transmute)] - // Clippy thinks these types are identical but is wrong here - transmute::< - ExecutionResult>, - ExecutionResult>, - >(mapped) - } + self.map_ref_with(ToOwnedTransparentlyMapper { span_range }) + .map(|x| x.0) } } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 51af0776..8d62f4ab 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -53,14 +53,19 @@ impl MapFromArgument for BeOwned { pub(crate) struct ToOwnedInfallibleMapper; impl RefLeafMapper for ToOwnedInfallibleMapper { - type OutputForm = BeOwned; - type ShortCircuit<'a> = Infallible; + type Output<'r, 'a: 'r, T: IsHierarchicalType> = MapperOutputContent<'static, T, BeOwned>; - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + fn to_parent_output<'r, 'a: 'r, T: IsChildType>( + output: Self::Output<'r, 'a, T>, + ) -> Self::Output<'r, 'a, T::ParentType> { + output.to_parent_output() + } + + fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r F::Leaf<'a, L>, - ) -> Result { - Ok(F::leaf_clone_to_owned_infallible(leaf)) + leaf: &'r ::Leaf<'a, T::Leaf>, + ) -> Self::Output<'r, 'a, T> { + MapperOutputContent::from_leaf(F::leaf_clone_to_owned_infallible(leaf)) } } @@ -69,11 +74,22 @@ pub(crate) struct ToOwnedTransparentlyMapper { } impl RefLeafMapper for ToOwnedTransparentlyMapper { - type OutputForm = BeOwned; - type ShortCircuit<'a> = ExecutionInterrupt; + type Output<'r, 'a: 'r, T: IsHierarchicalType> = + ExecutionResult>; - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>(self, leaf: &'r F::Leaf<'a, L>) -> ExecutionResult { - F::leaf_clone_to_owned_transparently(leaf, self.span_range) + fn to_parent_output<'r, 'a: 'r, T: IsChildType>( + output: Self::Output<'r, 'a, T>, + ) -> Self::Output<'r, 'a, T::ParentType> { + output.to_parent_output() + } + + fn map_leaf<'r, 'a: 'r, T: IsLeafType>( + self, + leaf: &'r ::Leaf<'a, T::Leaf>, + ) -> Self::Output<'r, 'a, T> { + Ok(MapperOutputContent::from_leaf( + F::leaf_clone_to_owned_transparently(leaf, self.span_range)?, + )) } } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index 7bfa16cb..69c7bf9d 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -34,47 +34,18 @@ impl LeafAsRefForm for BeMut { pub(crate) struct ToMutMapper; impl MutLeafMapper for ToMutMapper { - type OutputForm = BeMut; - type ShortCircuit<'a> = Infallible; + type Output<'r, 'a: 'r, T: IsHierarchicalType> = MapperOutputContent<'r, T, BeMut>; - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + fn to_parent_output<'r, 'a: 'r, T: IsChildType>( + output: Self::Output<'r, 'a, T>, + ) -> Self::Output<'r, 'a, T::ParentType> { + output.to_parent_output() + } + + fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r mut F::Leaf<'a, L>, - ) -> Result<&'r mut L, Infallible> { - Ok(F::leaf_as_mut(leaf)) + leaf: &'r mut F::Leaf<'a, T::Leaf>, + ) -> Self::Output<'r, 'a, T> { + MapperOutputContent::from_leaf(F::leaf_as_mut(leaf)) } } - -// impl> MutLeafMapper for ToMutMapper { -// type Output<'r, 'a: 'r> = AnyValueContent<'r, BeMut>; - -// fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( -// self, -// leaf: &'r mut F::Leaf<'a, L>, -// ) -> Self::Output<'r, 'a> -// where -// for<'x> &'x mut L: IsValueContent<'x, Form = BeMut>, -// for<'x> <&'x mut L as IsValueContent<'x>>::Type: UpcastTo, -// BeMut: for<'x> IsFormOf<<&'x mut L as IsValueContent<'x>>::Type, Content<'r> = &'r mut L>, -// { -// let my_mut = F::leaf_as_mut(leaf); -// <<&'r mut L as IsValueContent<'r>>::Type as UpcastTo>::upcast_to(my_mut) -// } -// } - -// impl> MutLeafMapper for ToMutMapper { -// type Output<'r, 'a: 'r> = AnyValueContent<'r, BeMut>; - -// fn map_leaf<'r, 'a: 'r, T: IsType, L: IsValueLeaf>( -// self, -// leaf: &'r mut F::Leaf<'a, L>, -// ) -> Self::Output<'r, 'a> -// where -// for<'x> &'x mut L: IsValueContent<'x, Form = BeMut, Type = T>, -// T: UpcastTo, -// BeMut: IsFormOf = &'r mut L>, -// { -// let my_mut = F::leaf_as_mut(leaf); -// <<&'r mut L as IsValueContent<'r>>::Type as UpcastTo>::upcast_to(my_mut) -// } -// } diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index d17a8dbf..e4301fa0 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -34,13 +34,18 @@ impl LeafAsRefForm for BeRef { pub(crate) struct ToRefMapper; impl RefLeafMapper for ToRefMapper { - type OutputForm = BeRef; - type ShortCircuit<'a> = Infallible; + type Output<'r, 'a: 'r, T: IsHierarchicalType> = MapperOutputContent<'r, T, BeRef>; - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + fn to_parent_output<'r, 'a: 'r, T: IsChildType>( + output: Self::Output<'r, 'a, T>, + ) -> Self::Output<'r, 'a, T::ParentType> { + output.to_parent_output() + } + + fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r F::Leaf<'a, L>, - ) -> Result<&'r L, Infallible> { - Ok(F::leaf_as_ref(leaf)) + leaf: &'r ::Leaf<'a, T::Leaf>, + ) -> Self::Output<'r, 'a, T> { + MapperOutputContent::from_leaf(F::leaf_as_ref(leaf)) } } diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index cc8c06f9..00065a06 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -11,89 +11,93 @@ pub(crate) trait LeafMapper { } pub(crate) trait RefLeafMapper { - type OutputForm: IsHierarchicalForm; - type ShortCircuit<'a>; + type Output<'r, 'a: 'r, T: IsHierarchicalType>: MapperOutput; + + fn to_parent_output<'r, 'a: 'r, T: IsChildType>( + output: Self::Output<'r, 'a, T>, + ) -> Self::Output<'r, 'a, T::ParentType>; - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r F::Leaf<'a, L>, - ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; + leaf: &'r F::Leaf<'a, T::Leaf>, + ) -> Self::Output<'r, 'a, T>; } pub(crate) trait MutLeafMapper { - type OutputForm: IsHierarchicalForm; - type ShortCircuit<'a>; + type Output<'r, 'a: 'r, T: IsHierarchicalType>: MapperOutput; - fn map_leaf<'r, 'a: 'r, L: IsValueLeaf>( + fn to_parent_output<'r, 'a: 'r, T: IsChildType>( + output: Self::Output<'r, 'a, T>, + ) -> Self::Output<'r, 'a, T::ParentType>; + + fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r mut F::Leaf<'a, L>, - ) -> Result<::Leaf<'r, L>, Self::ShortCircuit<'a>>; + leaf: &'r mut F::Leaf<'a, T::Leaf>, + ) -> Self::Output<'r, 'a, T>; } -// pub(crate) trait MutLeafMapper { -// type Output<'r, 'a: 'r>; - -// fn map_leaf<'r, 'a: 'r, T: IsType, L: IsValueLeaf>( -// self, -// leaf: &'r mut F::Leaf<'a, L>, -// ) -> Self::Output<'r, 'a> -// where -// for<'x> &'x mut L: IsValueContent<'x, Form = BeMut, Type = T>, -// T: UpcastTo, -// BeMut: IsFormOf = &'r mut L>, -// ; -// } - -// impl IsMutValueLeaf for L -// where -// for<'x> &'x mut L: IsValueContent<'x, Form = BeMut>, -// for<'x> <&'x mut L as IsValueContent<'x>>::Type: UpcastTo, -// { -// type MutType<'a> = <&'a mut L as IsValueContent<'a>>::Type; -// } - -// pub(crate) trait MutLeafMapper { -// type Output<'r, 'a: 'r, T>: MapperOutput; - -// fn map_leaf<'r, 'a: 'r, T: IsType, L: IsValueLeaf>( -// self, -// leaf: &'r mut F::Leaf<'a, L>, -// ) -> Self::Output<'r, 'a, T>; -// } - -// pub(crate) trait MapperOutput {} - -// pub(crate) trait MapperOutputToParent: MapperOutput { -// type ParentOutput: MapperOutput; - -// fn to_parent_output(self) -> Self::ParentOutput; -// } - -// pub(crate) struct MapperOutputValue(pub(crate) O); - -// impl MapperOutput for MapperOutputValue {} - -// impl MapperOutputToParent

for MapperOutputValue { -// type ParentOutput = Self; - -// fn to_parent_output(self) -> Self::ParentOutput { -// self -// } -// } - -// pub(crate) struct MapperOutputContent<'a, T: IsType, F: IsFormOf>(pub(crate) F::Content<'a>); - -// impl<'a, T: IsType, F: IsFormOf> MapperOutput for MapperOutputContent<'a, T, F> {} - -// impl<'a, P: IsHierarchicalType, C: IsChildType, F: IsHierarchicalForm + IsFormOf + IsFormOf

> -// MapperOutputToParent

for MapperOutputContent<'a, C, F> -// where -// for<'l> C: IsHierarchicalType = >::Content<'l>>, -// for<'l> P: IsHierarchicalType = >::Content<'l>>, -// { -// type ParentOutput = MapperOutputContent<'a, P, F>; - -// fn to_parent_output(self) -> Self::ParentOutput { -// MapperOutputContent::<'a, P, F>(C::into_parent::(self.0)) -// } -// } +pub(crate) trait MapperOutput { + type ParentOutput: MapperOutput<::ParentType> + where + T: IsChildType; + + fn to_parent_output(self) -> Self::ParentOutput + where + T: IsChildType; +} + +pub(crate) struct MapperOutputValue(pub(crate) O); + +impl MapperOutput for MapperOutputValue { + type ParentOutput + = Self + where + T: IsChildType; + + fn to_parent_output(self) -> Self::ParentOutput + where + T: IsChildType, + { + self + } +} + +pub(crate) struct MapperOutputContent<'a, T: IsHierarchicalType, F: IsHierarchicalForm>( + pub(crate) Content<'a, T, F>, +); + +impl<'a, T: IsLeafType, F: IsHierarchicalForm> MapperOutputContent<'a, T, F> { + pub(crate) fn from_leaf(leaf: F::Leaf<'a, T::Leaf>) -> Self { + Self(T::leaf_to_content(leaf)) + } +} + +impl<'a, T: IsHierarchicalType, F: IsHierarchicalForm> MapperOutput + for MapperOutputContent<'a, T, F> +{ + type ParentOutput + = MapperOutputContent<'a, T::ParentType, F> + where + T: IsChildType; + + fn to_parent_output(self) -> Self::ParentOutput + where + T: IsChildType, + { + MapperOutputContent::<'a, T::ParentType, F>(T::into_parent::(self.0)) + } +} + +impl> MapperOutput for ExecutionResult { + type ParentOutput + = ExecutionResult + where + T: IsChildType; + + fn to_parent_output(self) -> Self::ParentOutput + where + T: IsChildType, + { + self.map(|x| x.to_parent_output()) + } +} diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 4a673b89..8948b8fe 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -30,12 +30,12 @@ pub(crate) trait IsHierarchicalType: IsType { fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( mapper: M, content: &'r Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>>; + ) -> M::Output<'r, 'a, Self>; fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( mapper: M, content: &'r mut Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>>; + ) -> M::Output<'r, 'a, Self>; fn content_to_leaf_kind( content: &Self::Content<'_, F>, @@ -43,6 +43,12 @@ pub(crate) trait IsHierarchicalType: IsType { } pub(crate) trait IsLeafType: IsHierarchicalType { + type Leaf: IsValueLeaf; + + fn leaf_to_content<'a, F: IsHierarchicalForm>( + leaf: F::Leaf<'a, Self::Leaf>, + ) -> Self::Content<'a, F>; + fn leaf_kind() -> Self::LeafKind; } @@ -364,19 +370,23 @@ macro_rules! define_parent_type { fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( mapper: M, content: &'r Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>> { - Ok(match content { - $( $content::$variant(x) => $content::$variant(<$variant_type>::map_ref_with::<'r, 'a, F, M>(mapper, x)?), )* - }) + ) -> M::Output<'r, 'a, Self> { + match content { + $( $content::$variant(x) => M::to_parent_output::<'r, 'a, $variant_type>( + <$variant_type>::map_ref_with::<'r, 'a, F, M>(mapper, x) + ), )* + } } fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( mapper: M, content: &'r mut Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>> { - Ok(match content { - $( $content::$variant(x) => $content::$variant(<$variant_type>::map_mut_with::<'r, 'a, F, M>(mapper, x)?), )* - }) + ) -> M::Output<'r, 'a, Self> { + match content { + $( $content::$variant(x) => M::to_parent_output::<'r, 'a, $variant_type>( + <$variant_type>::map_mut_with::<'r, 'a, F, M>(mapper, x) + ), )* + } } fn content_to_leaf_kind( @@ -547,15 +557,15 @@ macro_rules! define_leaf_type { fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( mapper: M, content: &'r Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>> { - mapper.map_leaf::<$content_type>(content) + ) -> M::Output<'r, 'a, Self> { + mapper.map_leaf::(content) } fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( mapper: M, content: &'r mut Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>> { - mapper.map_leaf::<$content_type>(content) + ) -> M::Output<'r, 'a, Self> { + mapper.map_leaf::(content) } fn content_to_leaf_kind( @@ -566,6 +576,14 @@ macro_rules! define_leaf_type { } impl IsLeafType for $type_def { + type Leaf = $content_type; + + fn leaf_to_content<'a, F: IsHierarchicalForm>( + leaf: F::Leaf<'a, Self::Leaf>, + ) -> Self::Content<'a, F> { + leaf + } + fn leaf_kind() -> $kind { $kind } From ed3b87fd75359e45dea7812125b8c6705b01062b Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 04:11:33 +0000 Subject: [PATCH 40/58] refactor: Migrate owned mapping --- plans/TODO.md | 2 +- src/expressions/concepts/content.rs | 16 +++----- .../concepts/forms/referenceable.rs | 18 +++++---- src/expressions/concepts/mapping.rs | 12 +++--- src/expressions/concepts/type_traits.rs | 39 ++++++++++--------- src/internal_prelude.rs | 1 - 6 files changed, 44 insertions(+), 44 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 0907087f..30488507 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -249,7 +249,7 @@ First, read the @./2025-11-vision.md - [x] Attempt to improve mappers: - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. - [ ] Finish mapper improvements - - [ ] Migrate `self` + - [x] Migrate `self` - [ ] Consider if we even need `MapperOutput` or just some helper functions - [ ] Add a `Result` variant which will allow us to delay resolving the type kind into the error case when downcasting - [ ] Create macro to define inline mappers in various forms diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index a5b80a57..39c42be9 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -45,22 +45,18 @@ where self.upcast() } - fn map_with>( - self, - mapper: M, - ) -> Result, M::ShortCircuit<'a>> { + fn map_with>(self, mapper: M) -> M::Output<'a, Self::Type> { ::map_with::(mapper, self.into_content()) } fn into_referenceable(self) -> Content<'a, Self::Type, BeReferenceable> where - for<'l> OwnedToReferenceableMapper: - LeafMapper = Infallible>, + for<'l> OwnedToReferenceableMapper: LeafMapper< + Self::Form, + Output<'l, Self::Type> = MapperOutputContent<'l, Self::Type, BeReferenceable>, + >, { - match self.map_with(OwnedToReferenceableMapper) { - Ok(output) => output, - Err(infallible) => match infallible {}, // Need to include because of MSRV - } + self.map_with(OwnedToReferenceableMapper).0 } } diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index 61668b51..aa2110e9 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -29,13 +29,15 @@ impl MapFromArgument for BeReferenceable { pub(crate) struct OwnedToReferenceableMapper; impl LeafMapper for OwnedToReferenceableMapper { - type OutputForm = BeReferenceable; - type ShortCircuit<'a> = Infallible; - - fn map_leaf<'a, L: IsValueLeaf>( - self, - leaf: L, - ) -> Result>, Self::ShortCircuit<'a>> { - Ok(Rc::new(RefCell::new(leaf))) + type Output<'a, T: IsHierarchicalType> = MapperOutputContent<'a, T, BeReferenceable>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + output.to_parent_output() + } + + fn map_leaf<'a, T: IsLeafType>(self, leaf: T::Leaf) -> Self::Output<'a, T> { + MapperOutputContent::from_leaf(Rc::new(RefCell::new(leaf))) } } diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index 00065a06..8644acb2 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -1,13 +1,13 @@ use super::*; pub(crate) trait LeafMapper { - type OutputForm: IsHierarchicalForm; - type ShortCircuit<'a>; + type Output<'a, T: IsHierarchicalType>: MapperOutput; - fn map_leaf<'a, L: IsValueLeaf>( - self, - leaf: F::Leaf<'a, L>, - ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>>; + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType>; + + fn map_leaf<'a, T: IsLeafType>(self, leaf: F::Leaf<'a, T::Leaf>) -> Self::Output<'a, T>; } pub(crate) trait RefLeafMapper { diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 8948b8fe..717df1d9 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -25,7 +25,7 @@ pub(crate) trait IsHierarchicalType: IsType { fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( mapper: M, content: Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>>; + ) -> M::Output<'a, Self>; fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( mapper: M, @@ -361,10 +361,12 @@ macro_rules! define_parent_type { fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( mapper: M, content: Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>> { - Ok(match content { - $( $content::$variant(x) => $content::$variant(<$variant_type>::map_with::<'a, F, M>(mapper, x)?), )* - }) + ) -> M::Output<'a, Self> { + match content { + $( $content::$variant(x) => M::to_parent_output::<'a, $variant_type>( + <$variant_type>::map_with::<'a, F, M>(mapper, x) + ), )* + } } fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( @@ -550,8 +552,8 @@ macro_rules! define_leaf_type { fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( mapper: M, content: Self::Content<'a, F>, - ) -> Result, M::ShortCircuit<'a>> { - mapper.map_leaf::<$content_type>(content) + ) -> M::Output<'a, Self> { + mapper.map_leaf::(content) } fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( @@ -886,23 +888,24 @@ macro_rules! define_dyn_type { impl DynResolveFrom for $type_def { fn downcast_from<'a>(content: Content<'a, T, F>) -> Option> { - match T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content) { - Ok(_) => panic!("DynMapper is expected to always short-circuit"), - Err(dyn_leaf) => dyn_leaf, - } + T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content).0 } } impl LeafMapper for DynMapper<$dyn_type> { - type OutputForm = BeOwned; // Unused - type ShortCircuit<'a> = Option>; + type Output<'a, T: IsHierarchicalType> = MapperOutputValue>>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + output + } - fn map_leaf<'a, L: IsValueLeaf>( + fn map_leaf<'a, T: IsLeafType>( self, - leaf: F::Leaf<'a, L>, - ) -> Result<::Leaf<'a, L>, Self::ShortCircuit<'a>> - { - Err(F::leaf_to_dyn(leaf)) + leaf: F::Leaf<'a, T::Leaf>, + ) -> Self::Output<'a, T> { + MapperOutputValue(F::leaf_to_dyn(leaf)) } } }; diff --git a/src/internal_prelude.rs b/src/internal_prelude.rs index 39f60afa..c02b5ca4 100644 --- a/src/internal_prelude.rs +++ b/src/internal_prelude.rs @@ -5,7 +5,6 @@ pub(crate) use std::{ borrow::Cow, cell::{Ref, RefCell, RefMut}, collections::{BTreeMap, HashMap, HashSet}, - convert::Infallible, fmt::Debug, iter, marker::PhantomData, From 76e0d55844562692d11d798c0e793243ed2ea6b0 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 21:04:22 +0000 Subject: [PATCH 41/58] refactor: Remove MapperOutput --- plans/TODO.md | 2 +- src/expressions/concepts/content.rs | 11 ++- src/expressions/concepts/forms/owned.rs | 18 ++--- .../concepts/forms/referenceable.rs | 6 +- src/expressions/concepts/forms/simple_mut.rs | 6 +- src/expressions/concepts/forms/simple_ref.rs | 6 +- src/expressions/concepts/mapping.rs | 72 +------------------ src/expressions/concepts/type_traits.rs | 6 +- 8 files changed, 30 insertions(+), 97 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 30488507..374b57aa 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -250,7 +250,7 @@ First, read the @./2025-11-vision.md - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. - [ ] Finish mapper improvements - [x] Migrate `self` - - [ ] Consider if we even need `MapperOutput` or just some helper functions + - [x] Consider if we even need `MapperOutput` or just some helper functions - [ ] Add a `Result` variant which will allow us to delay resolving the type kind into the error case when downcasting - [ ] Create macro to define inline mappers in various forms - [ ] Create some macros to help build `self` / `&self` / `&mut self` methods across all contents of a form. Use `IsSelfCopyOnWriteContent` as an example to try implementing this diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 39c42be9..f4b3a3c1 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -53,10 +53,10 @@ where where for<'l> OwnedToReferenceableMapper: LeafMapper< Self::Form, - Output<'l, Self::Type> = MapperOutputContent<'l, Self::Type, BeReferenceable>, + Output<'l, Self::Type> = Content<'l, Self::Type, BeReferenceable>, >, { - self.map_with(OwnedToReferenceableMapper).0 + self.map_with(OwnedToReferenceableMapper) } } @@ -138,7 +138,7 @@ where 'a: 'r, Self::Form: LeafAsMutForm, { - self.map_mut_with(ToMutMapper).0 + self.map_mut_with(ToMutMapper) } fn as_ref_value<'r>(&'r self) -> Content<'r, Self::Type, BeRef> @@ -146,7 +146,7 @@ where 'a: 'r, Self::Form: LeafAsRefForm, { - self.map_ref_with(ToRefMapper).0 + self.map_ref_with(ToRefMapper) } /// This method should only be used when you are certain that the value should be cloned. @@ -158,7 +158,7 @@ where Self::Form: LeafAsRefForm, Self: Sized, { - self.map_ref_with(ToOwnedInfallibleMapper).0 + self.map_ref_with(ToOwnedInfallibleMapper) } /// A transparent clone is allowed for some types when doing method resolution. @@ -176,7 +176,6 @@ where Self: Sized, { self.map_ref_with(ToOwnedTransparentlyMapper { span_range }) - .map(|x| x.0) } } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 8d62f4ab..a3d17c03 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -53,19 +53,19 @@ impl MapFromArgument for BeOwned { pub(crate) struct ToOwnedInfallibleMapper; impl RefLeafMapper for ToOwnedInfallibleMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType> = MapperOutputContent<'static, T, BeOwned>; + type Output<'r, 'a: 'r, T: IsHierarchicalType> = Content<'static, T, BeOwned>; fn to_parent_output<'r, 'a: 'r, T: IsChildType>( output: Self::Output<'r, 'a, T>, ) -> Self::Output<'r, 'a, T::ParentType> { - output.to_parent_output() + T::into_parent(output) } fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, leaf: &'r ::Leaf<'a, T::Leaf>, ) -> Self::Output<'r, 'a, T> { - MapperOutputContent::from_leaf(F::leaf_clone_to_owned_infallible(leaf)) + T::leaf_to_content(F::leaf_clone_to_owned_infallible(leaf)) } } @@ -74,22 +74,22 @@ pub(crate) struct ToOwnedTransparentlyMapper { } impl RefLeafMapper for ToOwnedTransparentlyMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType> = - ExecutionResult>; + type Output<'r, 'a: 'r, T: IsHierarchicalType> = ExecutionResult>; fn to_parent_output<'r, 'a: 'r, T: IsChildType>( output: Self::Output<'r, 'a, T>, ) -> Self::Output<'r, 'a, T::ParentType> { - output.to_parent_output() + Ok(T::into_parent(output?)) } fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, leaf: &'r ::Leaf<'a, T::Leaf>, ) -> Self::Output<'r, 'a, T> { - Ok(MapperOutputContent::from_leaf( - F::leaf_clone_to_owned_transparently(leaf, self.span_range)?, - )) + Ok(T::leaf_to_content(F::leaf_clone_to_owned_transparently( + leaf, + self.span_range, + )?)) } } diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index aa2110e9..ecbe91eb 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -29,15 +29,15 @@ impl MapFromArgument for BeReferenceable { pub(crate) struct OwnedToReferenceableMapper; impl LeafMapper for OwnedToReferenceableMapper { - type Output<'a, T: IsHierarchicalType> = MapperOutputContent<'a, T, BeReferenceable>; + type Output<'a, T: IsHierarchicalType> = Content<'a, T, BeReferenceable>; fn to_parent_output<'a, T: IsChildType>( output: Self::Output<'a, T>, ) -> Self::Output<'a, T::ParentType> { - output.to_parent_output() + T::into_parent(output) } fn map_leaf<'a, T: IsLeafType>(self, leaf: T::Leaf) -> Self::Output<'a, T> { - MapperOutputContent::from_leaf(Rc::new(RefCell::new(leaf))) + T::leaf_to_content(Rc::new(RefCell::new(leaf))) } } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index 69c7bf9d..fe5dd7f2 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -34,18 +34,18 @@ impl LeafAsRefForm for BeMut { pub(crate) struct ToMutMapper; impl MutLeafMapper for ToMutMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType> = MapperOutputContent<'r, T, BeMut>; + type Output<'r, 'a: 'r, T: IsHierarchicalType> = Content<'r, T, BeMut>; fn to_parent_output<'r, 'a: 'r, T: IsChildType>( output: Self::Output<'r, 'a, T>, ) -> Self::Output<'r, 'a, T::ParentType> { - output.to_parent_output() + T::into_parent(output) } fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, leaf: &'r mut F::Leaf<'a, T::Leaf>, ) -> Self::Output<'r, 'a, T> { - MapperOutputContent::from_leaf(F::leaf_as_mut(leaf)) + T::leaf_to_content(F::leaf_as_mut(leaf)) } } diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index e4301fa0..404740ec 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -34,18 +34,18 @@ impl LeafAsRefForm for BeRef { pub(crate) struct ToRefMapper; impl RefLeafMapper for ToRefMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType> = MapperOutputContent<'r, T, BeRef>; + type Output<'r, 'a: 'r, T: IsHierarchicalType> = Content<'r, T, BeRef>; fn to_parent_output<'r, 'a: 'r, T: IsChildType>( output: Self::Output<'r, 'a, T>, ) -> Self::Output<'r, 'a, T::ParentType> { - output.to_parent_output() + T::into_parent(output) } fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, leaf: &'r ::Leaf<'a, T::Leaf>, ) -> Self::Output<'r, 'a, T> { - MapperOutputContent::from_leaf(F::leaf_as_ref(leaf)) + T::leaf_to_content(F::leaf_as_ref(leaf)) } } diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index 8644acb2..fc538877 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -1,7 +1,7 @@ use super::*; pub(crate) trait LeafMapper { - type Output<'a, T: IsHierarchicalType>: MapperOutput; + type Output<'a, T: IsHierarchicalType>; fn to_parent_output<'a, T: IsChildType>( output: Self::Output<'a, T>, @@ -11,7 +11,7 @@ pub(crate) trait LeafMapper { } pub(crate) trait RefLeafMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType>: MapperOutput; + type Output<'r, 'a: 'r, T: IsHierarchicalType>; fn to_parent_output<'r, 'a: 'r, T: IsChildType>( output: Self::Output<'r, 'a, T>, @@ -24,7 +24,7 @@ pub(crate) trait RefLeafMapper { } pub(crate) trait MutLeafMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType>: MapperOutput; + type Output<'r, 'a: 'r, T: IsHierarchicalType>; fn to_parent_output<'r, 'a: 'r, T: IsChildType>( output: Self::Output<'r, 'a, T>, @@ -35,69 +35,3 @@ pub(crate) trait MutLeafMapper { leaf: &'r mut F::Leaf<'a, T::Leaf>, ) -> Self::Output<'r, 'a, T>; } - -pub(crate) trait MapperOutput { - type ParentOutput: MapperOutput<::ParentType> - where - T: IsChildType; - - fn to_parent_output(self) -> Self::ParentOutput - where - T: IsChildType; -} - -pub(crate) struct MapperOutputValue(pub(crate) O); - -impl MapperOutput for MapperOutputValue { - type ParentOutput - = Self - where - T: IsChildType; - - fn to_parent_output(self) -> Self::ParentOutput - where - T: IsChildType, - { - self - } -} - -pub(crate) struct MapperOutputContent<'a, T: IsHierarchicalType, F: IsHierarchicalForm>( - pub(crate) Content<'a, T, F>, -); - -impl<'a, T: IsLeafType, F: IsHierarchicalForm> MapperOutputContent<'a, T, F> { - pub(crate) fn from_leaf(leaf: F::Leaf<'a, T::Leaf>) -> Self { - Self(T::leaf_to_content(leaf)) - } -} - -impl<'a, T: IsHierarchicalType, F: IsHierarchicalForm> MapperOutput - for MapperOutputContent<'a, T, F> -{ - type ParentOutput - = MapperOutputContent<'a, T::ParentType, F> - where - T: IsChildType; - - fn to_parent_output(self) -> Self::ParentOutput - where - T: IsChildType, - { - MapperOutputContent::<'a, T::ParentType, F>(T::into_parent::(self.0)) - } -} - -impl> MapperOutput for ExecutionResult { - type ParentOutput - = ExecutionResult - where - T: IsChildType; - - fn to_parent_output(self) -> Self::ParentOutput - where - T: IsChildType, - { - self.map(|x| x.to_parent_output()) - } -} diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 717df1d9..fb7e5ac9 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -888,12 +888,12 @@ macro_rules! define_dyn_type { impl DynResolveFrom for $type_def { fn downcast_from<'a>(content: Content<'a, T, F>) -> Option> { - T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content).0 + T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content) } } impl LeafMapper for DynMapper<$dyn_type> { - type Output<'a, T: IsHierarchicalType> = MapperOutputValue>>; + type Output<'a, T: IsHierarchicalType> = Option>; fn to_parent_output<'a, T: IsChildType>( output: Self::Output<'a, T>, @@ -905,7 +905,7 @@ macro_rules! define_dyn_type { self, leaf: F::Leaf<'a, T::Leaf>, ) -> Self::Output<'a, T> { - MapperOutputValue(F::leaf_to_dyn(leaf)) + F::leaf_to_dyn(leaf) } } }; From 29a43e58149802cc871804ec6c309ce2813a1519 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 21:38:33 +0000 Subject: [PATCH 42/58] refactor: Form leaf is now a function of leaf type --- src/expressions/concepts/content.rs | 4 +-- src/expressions/concepts/form.rs | 29 ++++++++++--------- src/expressions/concepts/forms/any_mut.rs | 17 ++++++----- src/expressions/concepts/forms/any_ref.rs | 15 ++++++---- src/expressions/concepts/forms/argument.rs | 2 +- src/expressions/concepts/forms/assignee.rs | 17 ++++++----- .../concepts/forms/copy_on_write.rs | 13 +++++---- src/expressions/concepts/forms/late_bound.rs | 13 +++++---- src/expressions/concepts/forms/mutable.rs | 17 ++++++----- src/expressions/concepts/forms/owned.rs | 21 ++++++++------ .../concepts/forms/referenceable.rs | 2 +- src/expressions/concepts/forms/shared.rs | 15 ++++++---- src/expressions/concepts/forms/simple_mut.rs | 17 ++++++----- src/expressions/concepts/forms/simple_ref.rs | 17 ++++++----- src/expressions/concepts/mapping.rs | 6 ++-- src/expressions/concepts/type_traits.rs | 11 ++++--- 16 files changed, 124 insertions(+), 92 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index f4b3a3c1..311f1722 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -26,7 +26,7 @@ where where Self::Type: UpcastTo, { - >::upcast_to(self.into_content()) + ::upcast_to(self.into_content()) } #[inline] @@ -46,7 +46,7 @@ where } fn map_with>(self, mapper: M) -> M::Output<'a, Self::Type> { - ::map_with::(mapper, self.into_content()) + ::map_with::(mapper, self.into_content()) } fn into_referenceable(self) -> Content<'a, Self::Type, BeReferenceable> diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 8f7f7683..de6ba2a1 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -15,23 +15,24 @@ pub(crate) trait IsForm: Sized + Clone {} pub(crate) trait IsHierarchicalForm: IsForm { /// The standard leaf for a hierachical type - type Leaf<'a, T: IsValueLeaf>; + /// Usually this will implement IsValueContent<'a, Type = T, Form = Self> + type Leaf<'a, T: IsLeafType>; } pub(crate) trait LeafAsRefForm: IsHierarchicalForm { - fn leaf_as_ref<'r, 'a: 'r, L: IsValueLeaf>(leaf: &'r Self::Leaf<'a, L>) -> &'r L; + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf; - fn leaf_clone_to_owned_infallible<'r, 'a: 'r, L: IsValueLeaf>( - leaf: &'r Self::Leaf<'a, L>, - ) -> L { + fn leaf_clone_to_owned_infallible<'r, 'a: 'r, T: IsLeafType>( + leaf: &'r Self::Leaf<'a, T>, + ) -> T::Leaf { Self::leaf_as_ref(leaf).clone() } - fn leaf_clone_to_owned_transparently<'r, 'a: 'r, L: IsValueLeaf>( - leaf: &'r Self::Leaf<'a, L>, + fn leaf_clone_to_owned_transparently<'r, 'a: 'r, T: IsLeafType>( + leaf: &'r Self::Leaf<'a, T>, error_span: SpanRange, - ) -> ExecutionResult { - let type_kind = ::Type::type_kind(); + ) -> ExecutionResult { + let type_kind = T::type_kind(); if type_kind.supports_transparent_cloning() { Ok(Self::leaf_clone_to_owned_infallible(leaf)) } else { @@ -44,7 +45,7 @@ pub(crate) trait LeafAsRefForm: IsHierarchicalForm { } pub(crate) trait LeafAsMutForm: IsHierarchicalForm { - fn leaf_as_mut<'r, 'a: 'r, L: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, L>) -> &'r mut L; + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf; } pub(crate) trait IsDynCompatibleForm: IsForm { @@ -55,9 +56,11 @@ pub(crate) trait IsDynCompatibleForm: IsForm { } pub(crate) trait IsDynMappableForm: IsHierarchicalForm + IsDynCompatibleForm { - fn leaf_to_dyn<'a, L: IsValueLeaf + CastDyn, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, L>, - ) -> Option>; + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> + where + T::Leaf: CastDyn; } pub(crate) trait MapFromArgument: IsHierarchicalForm { diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index 04f42ff5..46729e20 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -4,29 +4,32 @@ use super::*; pub(crate) struct BeAnyMut; impl IsForm for BeAnyMut {} impl IsHierarchicalForm for BeAnyMut { - type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyMut<'a, T>; + type Leaf<'a, T: IsLeafType> = crate::internal_prelude::AnyMut<'a, T::Leaf>; } impl IsDynCompatibleForm for BeAnyMut { - type DynLeaf<'a, T: 'static + ?Sized> = crate::internal_prelude::AnyMut<'a, T>; + type DynLeaf<'a, D: 'static + ?Sized> = crate::internal_prelude::AnyMut<'a, D>; } impl IsDynMappableForm for BeAnyMut { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> { - leaf.map_optional(T::map_mut) + ) -> Option> + where + T::Leaf: CastDyn, + { + leaf.map_optional(::map_mut) } } impl LeafAsRefForm for BeAnyMut { - fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { leaf } } impl LeafAsMutForm for BeAnyMut { - fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf { leaf } } diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 81f19109..f1ca1f2c 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -7,23 +7,26 @@ pub(crate) struct BeAnyRef; impl IsForm for BeAnyRef {} impl IsHierarchicalForm for BeAnyRef { - type Leaf<'a, T: IsValueLeaf> = crate::internal_prelude::AnyRef<'a, T>; + type Leaf<'a, T: IsLeafType> = crate::internal_prelude::AnyRef<'a, T::Leaf>; } impl IsDynCompatibleForm for BeAnyRef { - type DynLeaf<'a, T: 'static + ?Sized> = crate::internal_prelude::AnyRef<'a, T>; + type DynLeaf<'a, D: 'static + ?Sized> = crate::internal_prelude::AnyRef<'a, D>; } impl IsDynMappableForm for BeAnyRef { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized>( + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> { - leaf.map_optional(T::map_ref) + ) -> Option> + where + T::Leaf: CastDyn, + { + leaf.map_optional(::map_ref) } } impl LeafAsRefForm for BeAnyRef { - fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { leaf } } diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index 3553bc3d..280c346d 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -7,7 +7,7 @@ pub(crate) struct BeArgument; impl IsForm for BeArgument {} impl IsHierarchicalForm for BeArgument { - type Leaf<'a, T: IsValueLeaf> = ArgumentContent; + type Leaf<'a, T: IsLeafType> = ArgumentContent; } impl MapFromArgument for BeArgument { diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 6431b37a..f8b248b2 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -7,29 +7,32 @@ pub(crate) struct BeAssignee; impl IsForm for BeAssignee {} impl IsHierarchicalForm for BeAssignee { - type Leaf<'a, T: IsValueLeaf> = QqqAssignee; + type Leaf<'a, T: IsLeafType> = QqqAssignee; } impl IsDynCompatibleForm for BeAssignee { - type DynLeaf<'a, T: 'static + ?Sized> = QqqAssignee; + type DynLeaf<'a, D: 'static + ?Sized> = QqqAssignee; } impl IsDynMappableForm for BeAssignee { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> { - leaf.0.map_optional(T::map_mut).map(QqqAssignee) + ) -> Option> + where + T::Leaf: CastDyn, + { + leaf.0.map_optional(::map_mut).map(QqqAssignee) } } impl LeafAsRefForm for BeAssignee { - fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { &leaf.0 } } impl LeafAsMutForm for BeAssignee { - fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf { &mut leaf.0 } } diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 572bf4fd..f63582e7 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -7,17 +7,20 @@ pub(crate) struct BeCopyOnWrite; impl IsForm for BeCopyOnWrite {} impl IsHierarchicalForm for BeCopyOnWrite { - type Leaf<'a, T: IsValueLeaf> = CopyOnWriteContent; + type Leaf<'a, T: IsLeafType> = CopyOnWriteContent; } impl IsDynCompatibleForm for BeCopyOnWrite { - type DynLeaf<'a, T: 'static + ?Sized> = CopyOnWriteContent>; + type DynLeaf<'a, D: 'static + ?Sized> = CopyOnWriteContent>; } impl IsDynMappableForm for BeCopyOnWrite { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( - _leaf: Self::Leaf<'a, T>, - ) -> Option> { + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> + where + T::Leaf: CastDyn, + { // TODO: Add back once we add a map to CopyOnWriteContent todo!() } diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index 9ae72f2f..33ee4140 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -17,17 +17,20 @@ pub(crate) struct BeLateBound; impl IsForm for BeLateBound {} impl IsHierarchicalForm for BeLateBound { - type Leaf<'a, T: IsValueLeaf> = LateBoundContent; + type Leaf<'a, T: IsLeafType> = LateBoundContent; } impl IsDynCompatibleForm for BeLateBound { - type DynLeaf<'a, T: 'static + ?Sized> = LateBoundContent>; + type DynLeaf<'a, D: 'static + ?Sized> = LateBoundContent>; } impl IsDynMappableForm for BeLateBound { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( - _leaf: Self::Leaf<'a, T>, - ) -> Option> { + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Option> + where + T::Leaf: CastDyn, + { // TODO: Add back once we add a map to LateBoundContent todo!() } diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index 492e9bd3..e58d7199 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -7,29 +7,32 @@ pub(crate) struct BeMutable; impl IsForm for BeMutable {} impl IsHierarchicalForm for BeMutable { - type Leaf<'a, T: IsValueLeaf> = QqqMutable; + type Leaf<'a, T: IsLeafType> = QqqMutable; } impl IsDynCompatibleForm for BeMutable { - type DynLeaf<'a, T: 'static + ?Sized> = QqqMutable; + type DynLeaf<'a, D: 'static + ?Sized> = QqqMutable; } impl IsDynMappableForm for BeMutable { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> { - leaf.map_optional(T::map_mut) + ) -> Option> + where + T::Leaf: CastDyn, + { + leaf.map_optional(::map_mut) } } impl LeafAsRefForm for BeMutable { - fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { leaf } } impl LeafAsMutForm for BeMutable { - fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf { leaf } } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index a3d17c03..09bba581 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -13,29 +13,32 @@ pub(crate) struct BeOwned; impl IsForm for BeOwned {} impl IsHierarchicalForm for BeOwned { - type Leaf<'a, T: IsValueLeaf> = T; + type Leaf<'a, T: IsLeafType> = T::Leaf; } impl IsDynCompatibleForm for BeOwned { - type DynLeaf<'a, T: 'static + ?Sized> = Box; + type DynLeaf<'a, D: 'static + ?Sized> = Box; } impl IsDynMappableForm for BeOwned { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> { - T::map_boxed(Box::new(leaf)) + ) -> Option> + where + T::Leaf: CastDyn, + { + ::map_boxed(Box::new(leaf)) } } impl LeafAsRefForm for BeOwned { - fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { leaf } } impl LeafAsMutForm for BeOwned { - fn leaf_as_mut<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf { leaf } } @@ -63,7 +66,7 @@ impl RefLeafMapper for ToOwnedInfallibleMapper { fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r ::Leaf<'a, T::Leaf>, + leaf: &'r F::Leaf<'a, T>, ) -> Self::Output<'r, 'a, T> { T::leaf_to_content(F::leaf_clone_to_owned_infallible(leaf)) } @@ -84,7 +87,7 @@ impl RefLeafMapper for ToOwnedTransparentlyMapper { fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r ::Leaf<'a, T::Leaf>, + leaf: &'r ::Leaf<'a, T>, ) -> Self::Output<'r, 'a, T> { Ok(T::leaf_to_content(F::leaf_clone_to_owned_transparently( leaf, diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index ecbe91eb..ebc2c14c 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -12,7 +12,7 @@ pub(crate) struct BeReferenceable; impl IsForm for BeReferenceable {} impl IsHierarchicalForm for BeReferenceable { - type Leaf<'a, T: IsValueLeaf> = Rc>; + type Leaf<'a, T: IsLeafType> = Rc>; } impl MapFromArgument for BeReferenceable { diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 1a2ddf6a..8b2e2587 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -7,23 +7,26 @@ pub(crate) struct BeShared; impl IsForm for BeShared {} impl IsHierarchicalForm for BeShared { - type Leaf<'a, T: IsValueLeaf> = QqqShared; + type Leaf<'a, T: IsLeafType> = QqqShared; } impl IsDynCompatibleForm for BeShared { - type DynLeaf<'a, T: 'static + ?Sized> = QqqShared; + type DynLeaf<'a, D: 'static + ?Sized> = QqqShared; } impl IsDynMappableForm for BeShared { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> { - leaf.map_optional(T::map_ref) + ) -> Option> + where + T::Leaf: CastDyn, + { + leaf.map_optional(::map_ref) } } impl LeafAsRefForm for BeShared { - fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { leaf } } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index fe5dd7f2..dace411c 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -10,23 +10,26 @@ pub(crate) struct BeMut; impl IsForm for BeMut {} impl IsHierarchicalForm for BeMut { - type Leaf<'a, T: IsValueLeaf> = &'a mut T; + type Leaf<'a, T: IsLeafType> = &'a mut T::Leaf; } impl IsDynCompatibleForm for BeMut { - type DynLeaf<'a, T: 'static + ?Sized> = &'a mut T; + type DynLeaf<'a, D: 'static + ?Sized> = &'a mut D; } impl IsDynMappableForm for BeMut { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> { - T::map_mut(leaf) + ) -> Option> + where + T::Leaf: CastDyn, + { + ::map_mut(leaf) } } impl LeafAsRefForm for BeMut { - fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { leaf } } @@ -44,7 +47,7 @@ impl MutLeafMapper for ToMutMapper { fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r mut F::Leaf<'a, T::Leaf>, + leaf: &'r mut F::Leaf<'a, T>, ) -> Self::Output<'r, 'a, T> { T::leaf_to_content(F::leaf_as_mut(leaf)) } diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 404740ec..787ffbc1 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -10,23 +10,26 @@ pub(crate) struct BeRef; impl IsForm for BeRef {} impl IsHierarchicalForm for BeRef { - type Leaf<'a, T: IsValueLeaf> = &'a T; + type Leaf<'a, T: IsLeafType> = &'a T::Leaf; } impl IsDynCompatibleForm for BeRef { - type DynLeaf<'a, T: 'static + ?Sized> = &'a T; + type DynLeaf<'a, D: 'static + ?Sized> = &'a D; } impl IsDynMappableForm for BeRef { - fn leaf_to_dyn<'a, T: IsValueLeaf + CastDyn, D: ?Sized + 'static>( + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> { - T::map_ref(leaf) + ) -> Option> + where + T::Leaf: CastDyn, + { + ::map_ref(leaf) } } impl LeafAsRefForm for BeRef { - fn leaf_as_ref<'r, 'a: 'r, T: IsValueLeaf>(leaf: &'r Self::Leaf<'a, T>) -> &'r T { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { leaf } } @@ -44,7 +47,7 @@ impl RefLeafMapper for ToRefMapper { fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r ::Leaf<'a, T::Leaf>, + leaf: &'r ::Leaf<'a, T>, ) -> Self::Output<'r, 'a, T> { T::leaf_to_content(F::leaf_as_ref(leaf)) } diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index fc538877..2717e77d 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -7,7 +7,7 @@ pub(crate) trait LeafMapper { output: Self::Output<'a, T>, ) -> Self::Output<'a, T::ParentType>; - fn map_leaf<'a, T: IsLeafType>(self, leaf: F::Leaf<'a, T::Leaf>) -> Self::Output<'a, T>; + fn map_leaf<'a, T: IsLeafType>(self, leaf: F::Leaf<'a, T>) -> Self::Output<'a, T>; } pub(crate) trait RefLeafMapper { @@ -19,7 +19,7 @@ pub(crate) trait RefLeafMapper { fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r F::Leaf<'a, T::Leaf>, + leaf: &'r F::Leaf<'a, T>, ) -> Self::Output<'r, 'a, T>; } @@ -32,6 +32,6 @@ pub(crate) trait MutLeafMapper { fn map_leaf<'r, 'a: 'r, T: IsLeafType>( self, - leaf: &'r mut F::Leaf<'a, T::Leaf>, + leaf: &'r mut F::Leaf<'a, T>, ) -> Self::Output<'r, 'a, T>; } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index fb7e5ac9..c8a41d33 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -19,6 +19,7 @@ pub(crate) trait IsType: Sized { } pub(crate) trait IsHierarchicalType: IsType { + /// Typically this will implement `IsValueContent<'a, Type = Self, Form = F>` type Content<'a, F: IsHierarchicalForm>; type LeafKind: IsLeafKind; @@ -45,9 +46,7 @@ pub(crate) trait IsHierarchicalType: IsType { pub(crate) trait IsLeafType: IsHierarchicalType { type Leaf: IsValueLeaf; - fn leaf_to_content<'a, F: IsHierarchicalForm>( - leaf: F::Leaf<'a, Self::Leaf>, - ) -> Self::Content<'a, F>; + fn leaf_to_content<'a, F: IsHierarchicalForm>(leaf: F::Leaf<'a, Self>) -> Self::Content<'a, F>; fn leaf_kind() -> Self::LeafKind; } @@ -546,7 +545,7 @@ macro_rules! define_leaf_type { } impl IsHierarchicalType for $type_def { - type Content<'a, F: IsHierarchicalForm> = F::Leaf<'a, $content_type>; + type Content<'a, F: IsHierarchicalForm> = F::Leaf<'a, Self>; type LeafKind = $kind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( @@ -581,7 +580,7 @@ macro_rules! define_leaf_type { type Leaf = $content_type; fn leaf_to_content<'a, F: IsHierarchicalForm>( - leaf: F::Leaf<'a, Self::Leaf>, + leaf: F::Leaf<'a, Self>, ) -> Self::Content<'a, F> { leaf } @@ -903,7 +902,7 @@ macro_rules! define_dyn_type { fn map_leaf<'a, T: IsLeafType>( self, - leaf: F::Leaf<'a, T::Leaf>, + leaf: F::Leaf<'a, T>, ) -> Self::Output<'a, T> { F::leaf_to_dyn(leaf) } From 1af52d7b026208e3ba35e74e267b11d5d270975d Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 21:57:06 +0000 Subject: [PATCH 43/58] refactor: Slight perf improvement to resolving as ref --- src/expressions/concepts/content.rs | 2 +- src/expressions/concepts/type_traits.rs | 36 ++++++++++++++----------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 311f1722..b25d53fe 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -34,7 +34,7 @@ where where U: DowncastFrom, { - U::downcast_from(self.into_content()) + U::downcast_from(self.into_content()).ok() } #[inline] diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index c8a41d33..df6c8dec 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -64,23 +64,25 @@ pub(crate) trait UpcastTo: pub(crate) trait DowncastFrom: IsHierarchicalType { - fn downcast_from<'a>(content: Content<'a, T, F>) -> Option>; + fn downcast_from<'a>( + content: Content<'a, T, F>, + ) -> Result, Content<'a, T, F>>; fn resolve<'a>( content: Content<'a, T, F>, span_range: SpanRange, resolution_target: &str, ) -> ExecutionResult> { - let leaf_kind = T::content_to_leaf_kind::(&content); let content = match Self::downcast_from(content) { - Some(c) => c, - None => { + Ok(c) => c, + Err(existing) => { + let leaf_kind = T::content_to_leaf_kind::(&existing); return span_range.value_err(format!( "{} is expected to be {}, but it is {}", resolution_target, Self::ARTICLED_DISPLAY_NAME, leaf_kind.articled_display_name(), - )) + )); } }; Ok(content) @@ -118,11 +120,11 @@ pub(crate) trait IsChildType: IsHierarchicalType { fn into_parent<'a, F: IsHierarchicalForm>( content: Self::Content<'a, F>, - ) -> ::Content<'a, F>; + ) -> Content<'a, Self::ParentType, F>; fn from_parent<'a, F: IsHierarchicalForm>( content: ::Content<'a, F>, - ) -> Option>; + ) -> Result, Content<'a, Self::ParentType, F>>; } macro_rules! impl_type_feature_resolver { @@ -177,8 +179,8 @@ macro_rules! impl_ancestor_chain_conversions { { fn downcast_from<'a>( content: Content<'a, Self, F>, - ) -> Option> { - Some(content) + ) -> Result, Content<'a, Self, F>> { + Ok(content) } } @@ -203,10 +205,10 @@ macro_rules! impl_ancestor_chain_conversions { fn from_parent<'a, F: IsHierarchicalForm>( content: ::Content<'a, F>, - ) -> Option> { + ) -> Result, Content<'a, Self::ParentType, F>> { match content { - $parent_content::$parent_variant(i) => Some(i), - _ => None, + $parent_content::$parent_variant(i) => Ok(i), + other => Err(other), } } } @@ -215,7 +217,7 @@ macro_rules! impl_ancestor_chain_conversions { { fn downcast_from<'a>( content: Content<'a, $parent, F>, - ) -> Option> { + ) -> Result, Content<'a, $parent, F>> { <$child as IsChildType>::from_parent(content) } } @@ -233,8 +235,12 @@ macro_rules! impl_ancestor_chain_conversions { impl DowncastFrom<$ancestor, F> for $child { fn downcast_from<'a>( content: Content<'a, $ancestor, F>, - ) -> Option> { - <$child as DowncastFrom<$parent, F>>::downcast_from(<$parent as DowncastFrom<$ancestor, F>>::downcast_from(content)?) + ) -> Result, Content<'a, $ancestor, F>> { + let inner = <$parent as DowncastFrom<$ancestor, F>>::downcast_from(content)?; + match <$child as DowncastFrom<$parent, F>>::downcast_from(inner) { + Ok(c) => Ok(c), + Err(existing) => Err(<$parent as UpcastTo<$ancestor, F>>::upcast_to(existing)), + } } } From 03a84acb88580aeaf2be6205234e9ede1e9d4dc0 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 21:57:34 +0000 Subject: [PATCH 44/58] refactor: Remove downcast_resolve_spanned in favour of downcast_resolve --- src/expressions/concepts/content.rs | 42 ++++++++++++++++++----------- src/expressions/values/object.rs | 4 +-- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index b25d53fe..a0b1ef15 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -15,6 +15,30 @@ pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self; } +pub(crate) trait FromSpannedValueContent<'a>: IsValueContent<'a> { + fn from_spanned_content(content: Spanned>) -> Self; +} + +impl<'a, C: FromValueContent<'a>> FromSpannedValueContent<'a> for C { + fn from_spanned_content(content: Spanned>) -> Self { + let Spanned(inner, _span_range) = content; + C::from_content(inner) + } +} + +impl<'a, C: IsValueContent<'a>> IsValueContent<'a> for Spanned { + type Type = C::Type; + type Form = C::Form; +} + +impl<'a, C: FromValueContent<'a>> FromSpannedValueContent<'a> for Spanned { + fn from_spanned_content( + Spanned(content, span_range): Spanned>, + ) -> Self { + Spanned(C::from_content(content), span_range) + } +} + pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> where Self: Sized, @@ -66,7 +90,7 @@ where C::Type: IsHierarchicalType, C::Form: IsHierarchicalForm, { - pub(crate) fn downcast_resolve>( + pub(crate) fn downcast_resolve>( self, description: &str, ) -> ExecutionResult @@ -77,21 +101,7 @@ where let content = value.into_content(); let resolved = <>::Type>::resolve(content, span_range, description)?; - Ok(X::from_content(resolved)) - } - - pub(crate) fn downcast_resolve_spanned>( - self, - description: &str, - ) -> ExecutionResult> - where - >::Type: DowncastFrom, - { - let span_range = self.1; - Ok(Spanned( - self.downcast_resolve::(description)?, - span_range, - )) + Ok(X::from_spanned_content(Spanned(resolved, span_range))) } } diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index 10add3ad..921ed5f4 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -77,12 +77,12 @@ impl ObjectValue { index: Spanned, auto_create: bool, ) -> ExecutionResult<&mut AnyValue> { - let index: Spanned<&str> = index.downcast_resolve_spanned("An object key")?; + let index: Spanned<&str> = index.downcast_resolve("An object key")?; self.mut_entry(index.map(|s| s.to_string()), auto_create) } pub(super) fn index_ref(&self, index: Spanned) -> ExecutionResult<&AnyValue> { - let key: Spanned<&str> = index.downcast_resolve_spanned("An object key")?; + let key: Spanned<&str> = index.downcast_resolve("An object key")?; let entry = self.entries.get(*key).ok_or_else(|| { key.value_error(format!("The object does not have a field named `{}`", *key)) })?; From 054d9001e83028781f65dc8bd8640accd76ccd0b Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 11 Jan 2026 21:59:54 +0000 Subject: [PATCH 45/58] refactor: `downcast` returns a result too --- src/expressions/concepts/content.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index a0b1ef15..a3ca6568 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -54,11 +54,12 @@ where } #[inline] - fn downcast(self) -> Option> + #[allow(clippy::type_complexity)] // It's actually pretty readable + fn downcast(self) -> Result, Content<'a, Self::Type, Self::Form>> where U: DowncastFrom, { - U::downcast_from(self.into_content()).ok() + U::downcast_from(self.into_content()) } #[inline] From de83c1a19f96a7a374fb5fac75d1de4e5606bcf2 Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 12 Jan 2026 01:40:49 +0000 Subject: [PATCH 46/58] refactor: Create `map_via_leaf` for simpler leaf map code --- plans/TODO.md | 7 +- src/expressions/concepts/content.rs | 65 ++++---- .../concepts/forms/copy_on_write.rs | 64 +++++--- src/expressions/concepts/forms/owned.rs | 43 ------ .../concepts/forms/referenceable.rs | 16 -- src/expressions/concepts/forms/simple_mut.rs | 19 --- src/expressions/concepts/forms/simple_ref.rs | 19 --- src/expressions/concepts/mapping.rs | 140 ++++++++++++++++++ 8 files changed, 223 insertions(+), 150 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 374b57aa..3554caee 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -248,11 +248,12 @@ First, read the @./2025-11-vision.md - [ ] Stage 2 of the form migration: - [x] Attempt to improve mappers: - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. - - [ ] Finish mapper improvements + - [x] Finish mapper improvements - [x] Migrate `self` - [x] Consider if we even need `MapperOutput` or just some helper functions - - [ ] Add a `Result` variant which will allow us to delay resolving the type kind into the error case when downcasting - - [ ] Create macro to define inline mappers in various forms + - [x] Delay resolving the type kind into the error case when downcasting + - [x] Create macro to define inline mappers in various forms + - [ ] Reproduce `CopyOnWrite` - [ ] Create some macros to help build `self` / `&self` / `&mut self` methods across all contents of a form. Use `IsSelfCopyOnWriteContent` as an example to try implementing this - [ ] Get rid of `CopyOnWrite`, have only CopyOnWriteValue - [ ] Get rid of `Shared`, `Mutable` and `Assignee`, have only `AnyValueMutable` or other named kinds diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index a3ca6568..dc9f7536 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -70,18 +70,16 @@ where self.upcast() } - fn map_with>(self, mapper: M) -> M::Output<'a, Self::Type> { - ::map_with::(mapper, self.into_content()) - } - fn into_referenceable(self) -> Content<'a, Self::Type, BeReferenceable> where - for<'l> OwnedToReferenceableMapper: LeafMapper< - Self::Form, - Output<'l, Self::Type> = Content<'l, Self::Type, BeReferenceable>, - >, + Self: IsValueContent<'a, Form = BeOwned>, { - self.map_with(OwnedToReferenceableMapper) + map_via_leaf! { + input: (Content<'a, Self::Type, Self::Form>) = self.into_content(), + fn map_leaf(leaf) -> (Content<'a, T, BeReferenceable>) { + T::leaf_to_content(Rc::new(RefCell::new(leaf))) + } + } } } @@ -124,32 +122,17 @@ where Self::Type: IsHierarchicalType = Self>, Self::Form: IsHierarchicalForm, { - fn map_mut_with<'r, M: MutLeafMapper>( - &'r mut self, - mapper: M, - ) -> M::Output<'r, 'a, Self::Type> - where - 'a: 'r, - { - ::map_mut_with::(mapper, self) - } - - fn map_ref_with<'r, M: RefLeafMapper>( - &'r self, - mapper: M, - ) -> M::Output<'r, 'a, Self::Type> - where - 'a: 'r, - { - ::map_ref_with::(mapper, self) - } - fn as_mut_value<'r>(&'r mut self) -> Content<'r, Self::Type, BeMut> where 'a: 'r, Self::Form: LeafAsMutForm, { - self.map_mut_with(ToMutMapper) + map_via_leaf! { + input: &'r mut (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (Content<'r, T, BeMut>) { + T::leaf_to_content(F::leaf_as_mut(leaf)) + } + } } fn as_ref_value<'r>(&'r self) -> Content<'r, Self::Type, BeRef> @@ -157,7 +140,12 @@ where 'a: 'r, Self::Form: LeafAsRefForm, { - self.map_ref_with(ToRefMapper) + map_via_leaf! { + input: &'r (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (Content<'r, T, BeRef>) { + T::leaf_to_content(F::leaf_as_ref(leaf)) + } + } } /// This method should only be used when you are certain that the value should be cloned. @@ -169,7 +157,12 @@ where Self::Form: LeafAsRefForm, Self: Sized, { - self.map_ref_with(ToOwnedInfallibleMapper) + map_via_leaf! { + input: &'r (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { + T::leaf_to_content(F::leaf_clone_to_owned_infallible(leaf)) + } + } } /// A transparent clone is allowed for some types when doing method resolution. @@ -186,7 +179,13 @@ where Self::Form: LeafAsRefForm, Self: Sized, { - self.map_ref_with(ToOwnedTransparentlyMapper { span_range }) + map_via_leaf! { + input: &'r (Content<'a, Self::Type, Self::Form>) = self, + state: SpanRange | let span_range = span_range, + fn map_leaf(leaf) -> (ExecutionResult>) { + Ok(T::leaf_to_content(F::leaf_clone_to_owned_transparently(leaf, span_range)?)) + } + } } } diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index f63582e7..0fce3529 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -1,7 +1,5 @@ use super::*; -pub(crate) type QqqCopyOnWrite = Content<'static, T, BeCopyOnWrite>; - #[derive(Copy, Clone)] pub(crate) struct BeCopyOnWrite; impl IsForm for BeCopyOnWrite {} @@ -37,6 +35,29 @@ impl MapFromArgument for BeCopyOnWrite { } } +impl LeafAsRefForm for BeCopyOnWrite { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + match leaf { + CopyOnWriteContent::Owned(owned) => owned, + CopyOnWriteContent::SharedWithInfallibleCloning(shared) => shared, + CopyOnWriteContent::SharedWithTransparentCloning(shared) => shared, + } + } + + fn leaf_clone_to_owned_infallible<'r, 'a: 'r, T: IsLeafType>( + leaf: &'r Self::Leaf<'a, T>, + ) -> T::Leaf { + todo!("Need to copy the custom implementation") + } + + fn leaf_clone_to_owned_transparently<'r, 'a: 'r, T: IsLeafType>( + leaf: &'r Self::Leaf<'a, T>, + error_span: SpanRange, + ) -> ExecutionResult { + todo!("Need to copy the custom implementation") + } +} + /// Typically O == T or more specifically, ::Owned pub(crate) enum CopyOnWriteContent { /// An owned value that can be used directly @@ -49,19 +70,28 @@ pub(crate) enum CopyOnWriteContent { SharedWithTransparentCloning(Shared), } -// pub(crate) trait IsSelfCopyOnWriteContent<'a>: IsSelfValueContent<'a> -// where -// Self: IsValueContent<'a, Form = BeCopyOnWrite>, -// BeCopyOnWrite: IsFormOf<>::Type, Content<'a> = Self>, -// { -// fn acts_as_shared_reference(&self) -> bool { -// struct ThisMapper; -// impl -// self.map_ref_with(mapper) -// } -// } +pub(crate) trait IsSelfCopyOnWriteContent<'a>: IsSelfValueContent<'a> +where + Self: IsValueContent<'a, Form = BeCopyOnWrite>, + Self::Type: IsHierarchicalType = Self>, +{ + fn acts_as_shared_reference(&self) -> bool { + map_via_leaf! { + input: &'r (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (bool) { + match leaf { + CopyOnWriteContent::Owned(_) => false, + CopyOnWriteContent::SharedWithInfallibleCloning(_) => false, + CopyOnWriteContent::SharedWithTransparentCloning(_) => true, + } + } + } + } +} -// impl<'a, C: IsSelfValueContent<'a>> IsSelfCopyOnWriteContent<'a> for A -// where -// Self: IsValueContent<'a, Form = BeCopyOnWrite>, -// {} +impl<'a, C: IsSelfValueContent<'a>> IsSelfCopyOnWriteContent<'a> for C +where + Self: IsValueContent<'a, Form = BeCopyOnWrite>, + Self::Type: IsHierarchicalType = Self>, +{ +} diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 09bba581..467b1086 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -53,49 +53,6 @@ impl MapFromArgument for BeOwned { } } -pub(crate) struct ToOwnedInfallibleMapper; - -impl RefLeafMapper for ToOwnedInfallibleMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType> = Content<'static, T, BeOwned>; - - fn to_parent_output<'r, 'a: 'r, T: IsChildType>( - output: Self::Output<'r, 'a, T>, - ) -> Self::Output<'r, 'a, T::ParentType> { - T::into_parent(output) - } - - fn map_leaf<'r, 'a: 'r, T: IsLeafType>( - self, - leaf: &'r F::Leaf<'a, T>, - ) -> Self::Output<'r, 'a, T> { - T::leaf_to_content(F::leaf_clone_to_owned_infallible(leaf)) - } -} - -pub(crate) struct ToOwnedTransparentlyMapper { - pub(crate) span_range: SpanRange, -} - -impl RefLeafMapper for ToOwnedTransparentlyMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType> = ExecutionResult>; - - fn to_parent_output<'r, 'a: 'r, T: IsChildType>( - output: Self::Output<'r, 'a, T>, - ) -> Self::Output<'r, 'a, T::ParentType> { - Ok(T::into_parent(output?)) - } - - fn map_leaf<'r, 'a: 'r, T: IsLeafType>( - self, - leaf: &'r ::Leaf<'a, T>, - ) -> Self::Output<'r, 'a, T> { - Ok(T::leaf_to_content(F::leaf_clone_to_owned_transparently( - leaf, - self.span_range, - )?)) - } -} - #[cfg(test)] mod test { use super::*; diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index ebc2c14c..f92b69c1 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -25,19 +25,3 @@ impl MapFromArgument for BeReferenceable { todo!() } } - -pub(crate) struct OwnedToReferenceableMapper; - -impl LeafMapper for OwnedToReferenceableMapper { - type Output<'a, T: IsHierarchicalType> = Content<'a, T, BeReferenceable>; - - fn to_parent_output<'a, T: IsChildType>( - output: Self::Output<'a, T>, - ) -> Self::Output<'a, T::ParentType> { - T::into_parent(output) - } - - fn map_leaf<'a, T: IsLeafType>(self, leaf: T::Leaf) -> Self::Output<'a, T> { - T::leaf_to_content(Rc::new(RefCell::new(leaf))) - } -} diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index dace411c..bc5b5589 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -33,22 +33,3 @@ impl LeafAsRefForm for BeMut { leaf } } - -pub(crate) struct ToMutMapper; - -impl MutLeafMapper for ToMutMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType> = Content<'r, T, BeMut>; - - fn to_parent_output<'r, 'a: 'r, T: IsChildType>( - output: Self::Output<'r, 'a, T>, - ) -> Self::Output<'r, 'a, T::ParentType> { - T::into_parent(output) - } - - fn map_leaf<'r, 'a: 'r, T: IsLeafType>( - self, - leaf: &'r mut F::Leaf<'a, T>, - ) -> Self::Output<'r, 'a, T> { - T::leaf_to_content(F::leaf_as_mut(leaf)) - } -} diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 787ffbc1..dc75e2cc 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -33,22 +33,3 @@ impl LeafAsRefForm for BeRef { leaf } } - -pub(crate) struct ToRefMapper; - -impl RefLeafMapper for ToRefMapper { - type Output<'r, 'a: 'r, T: IsHierarchicalType> = Content<'r, T, BeRef>; - - fn to_parent_output<'r, 'a: 'r, T: IsChildType>( - output: Self::Output<'r, 'a, T>, - ) -> Self::Output<'r, 'a, T::ParentType> { - T::into_parent(output) - } - - fn map_leaf<'r, 'a: 'r, T: IsLeafType>( - self, - leaf: &'r ::Leaf<'a, T>, - ) -> Self::Output<'r, 'a, T> { - T::leaf_to_content(F::leaf_as_ref(leaf)) - } -} diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index 2717e77d..6d39206b 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -35,3 +35,143 @@ pub(crate) trait MutLeafMapper { leaf: &'r mut F::Leaf<'a, T>, ) -> Self::Output<'r, 'a, T>; } + +/// Macro for creating and immediately applying a leaf mapper inline. +/// +/// This macro creates an anonymous mapper struct, implements the appropriate mapper trait +/// (LeafMapper, RefLeafMapper, or MutLeafMapper) based on the leaf pattern, and applies +/// it to the input content. +/// +/// Specific output type forms are supported for parent propogation. +/// If you get a cryptic error in the to_parent method, you may need to add +/// an explicit implementation into [`__map_via_leaf_to_parent`] +/// +/// # Syntax +/// +/// ```ignore +/// map_via_leaf! { +/// input: [ | &'r | &'r mut] (Content<'a, InputType, InputForm>) = input_value, +/// state: StateType | let state_binding = state_init, // Optional +/// fn map_leaf | : | = ], T>(leaf) -> (OutputType) +/// [where <...>] // Optional +/// { +/// // mapper body +/// } +/// } +/// ``` +macro_rules! map_via_leaf { + ( + input: $(&$r:lifetime $($mut:ident)?)? (Content<$a:lifetime, $input_type:ty, $input_form:ty>) = $input_value:expr, + $(state: $state_type:ty | let $state_pat:pat = $state_init:expr,)? + fn map_leaf <$f:ident $(: $fb:ident $(+ $fbe:ident )*)? $(= $fixed_form:ty)?, $t:ident> ($leaf:ident) -> ($($output_type:tt)+) + $(where $($where_clause:tt)*)? + $body:block + ) => {{ + struct __InlineMapper { + state: $crate::if_exists!{ {$($state_type)?} {$($state_type)?} {()} }, + } + + $crate::__map_via_leaf_trait_impl!{ + $(@fixed_form $fixed_form |)? impl<$f $(: $fb $(+ $fbe)*)?> @mutability $(&$r $($mut)?)? for __InlineMapper $(where $($where_clause)*)? + { + type Output<$($r,)? $a $(: $r)?, $t: $crate::expressions::concepts::IsHierarchicalType> = $($output_type)+; + + fn to_parent_output<$($r,)? $a $(: $r)?, $t: $crate::expressions::concepts::IsChildType>( + output: Self::Output<$($r,)? $a, $t>, + ) -> Self::Output<$($r,)? $a, $t::ParentType> { + $crate::__map_via_leaf_to_parent!(output -> $($output_type)+) + } + + #[allow(unused_mut)] + fn map_leaf<$($r,)? $a $(: $r)?, $t: $crate::expressions::concepts::IsLeafType>( + self, + $leaf: $crate::__map_via_leaf_leaf_type!($(@fixed_form $fixed_form |)? $(&$r $($mut)?)? | $f::Leaf<$a, $t>), + ) -> Self::Output<$($r,)? 'a, T> { + $(let $state_pat = self.state;)? + $body + } + } + }; + + let __mapper = __InlineMapper { + state: $crate::if_exists!{ {$($state_type)?} {$($state_init)?} {()} }, + }; + $crate::__map_via_leaf_output!( + <$input_type, $input_form> @mutability $(&$r $($mut)?)? ( + __mapper, + $input_value + ) + ) + }}; +} + +#[doc(hidden)] +macro_rules! __map_via_leaf_trait_impl { + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime mut for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl <$f $(: $fb $(+ $fbe)*)?> MutLeafMapper<$f> for $mapper $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl <$f $(: $fb $(+ $fbe)*)?> RefLeafMapper<$f> for $mapper $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl <$f $(: $fb $(+ $fbe)*)?> LeafMapper<$f> for $mapper $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime mut for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl MutLeafMapper<$fixed_form> for $mapper $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl RefLeafMapper<$fixed_form> for $mapper $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl LeafMapper<$fixed_form> for $mapper $(where $($where_clause)*)? { $($body)* } }; +} + +#[doc(hidden)] +macro_rules! __map_via_leaf_output { + (<$input_type:ty, $input_form:ty> @mutability &$r:lifetime mut ($mapper:expr, $input:expr) ) => { + <$input_type>::map_mut_with::<$input_form, _>($mapper, $input) + }; + (<$input_type:ty, $input_form:ty> @mutability &$r:lifetime ($mapper:expr, $input:expr) ) => { + <$input_type>::map_ref_with::<$input_form, _>($mapper, $input) + }; + (<$input_type:ty, $input_form:ty> @mutability ($mapper:expr, $input:expr) ) => { + <$input_type>::map_with::<$input_form, _>($mapper, $input) + }; +} + +#[doc(hidden)] +macro_rules! __map_via_leaf_leaf_type { + (@fixed_form $fixed_form:ty | $(&$r:lifetime $($mut:ident)?)? | $f:ident::Leaf<$a:lifetime, $t:ident>) => { + $(&$r $($mut)?)? <$fixed_form as IsHierarchicalForm>::Leaf<$a, $t> + }; + ($(&$r:lifetime $($mut:ident)?)? | $f:ident::Leaf<$a:lifetime, $t:ident>) => { + $(&$r $($mut)?)? <$f>::Leaf<$a, $t> + }; +} + +#[doc(hidden)] +macro_rules! __map_via_leaf_to_parent { + ($output:ident -> Content<$lt:lifetime, $t:ident, $form:ty>) => { + $t::into_parent($output) + }; + ($output:ident -> ExecutionResult>) => { + Ok($t::into_parent($output?)) + }; + ($output:ident -> Result, $err:ty>) => { + Ok($t::into_parent($output?)) + }; + ($output:ident -> $ty:ty) => { + $output + }; +} + +pub(crate) use { + __map_via_leaf_leaf_type, __map_via_leaf_output, __map_via_leaf_to_parent, + __map_via_leaf_trait_impl, map_via_leaf, +}; + +#[cfg(test)] +mod test_macro { + use super::*; + + #[test] + fn test_map_via_leaf_owned() { + let mut x = 4; + let owned = map_via_leaf! { + input: &'r mut (Content<'a, U8Type, BeOwned>) = &mut x, + fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { + T::leaf_to_content(F::leaf_clone_to_owned_infallible(leaf)) + } + }; + assert_eq!(owned, 4); + } +} From 63312768b7cbb3c1e501071183936ea0d3abcee0 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 15 Jan 2026 02:18:26 +0000 Subject: [PATCH 47/58] refactor: Blanket impl leaf / content conversion for various forms --- plans/TODO.md | 16 +- src/expressions/concepts/content.rs | 21 +- src/expressions/concepts/forms/any_mut.rs | 21 +- src/expressions/concepts/forms/any_ref.rs | 17 +- src/expressions/concepts/forms/argument.rs | 20 +- src/expressions/concepts/forms/assignee.rs | 17 ++ .../concepts/forms/copy_on_write.rs | 247 +++++++++++++++--- src/expressions/concepts/forms/late_bound.rs | 36 +-- src/expressions/concepts/forms/mutable.rs | 17 ++ src/expressions/concepts/forms/owned.rs | 19 +- .../concepts/forms/referenceable.rs | 32 ++- src/expressions/concepts/forms/shared.rs | 17 ++ src/expressions/concepts/forms/simple_mut.rs | 17 +- src/expressions/concepts/forms/simple_ref.rs | 17 +- src/expressions/concepts/mapping.rs | 11 + src/expressions/concepts/type_traits.rs | 169 +++--------- src/expressions/values/string.rs | 2 +- 17 files changed, 455 insertions(+), 241 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index 3554caee..b118778d 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -253,11 +253,15 @@ First, read the @./2025-11-vision.md - [x] Consider if we even need `MapperOutput` or just some helper functions - [x] Delay resolving the type kind into the error case when downcasting - [x] Create macro to define inline mappers in various forms + - [ ] Attempt to see if I can get rid of needing `leaf_to_content` and maybe `content_to_leaf` by exploiting a trick to bind associated types via a sub-trait (e.g. as I did with `IsValueContent::LeafType> + IsLeafValueContent`) - [ ] Reproduce `CopyOnWrite` - - [ ] Create some macros to help build `self` / `&self` / `&mut self` methods across all contents of a form. Use `IsSelfCopyOnWriteContent` as an example to try implementing this - - [ ] Get rid of `CopyOnWrite`, have only CopyOnWriteValue - - [ ] Get rid of `Shared`, `Mutable` and `Assignee`, have only `AnyValueMutable` or other named kinds - - [ ] Migrate `Shared`, `Mutable`, `Assignee` + - [ ] Implement `TODO[concepts]: COPY ON WRITE MAPPING` + - [ ] `BeCopyOnWrite::owned()` / `shared_as_..` can go via `AnyLevelCopyOnWrite => into_copy_on_write` + - [ ] Reproduce `Shared` methods etc + - [ ] Reproduce `Mutable` methods etc + - [ ] Reproduce `Assignee` methods etc + - [ ] Change `CopyOnWrite` => `CopyOnWriteAnyValue` similarly with `Shared`, `Mutable` and `Assignee` + - [ ] Migrate `CopyOnWrite`, `Shared`, `Mutable`, `Assignee` - [ ] `Shared` only works for leaves - [ ] For specific parents, use e.g. `AnyValueShared` - [ ] If you need something to apply across all leaves, implement it on some trait @@ -279,6 +283,10 @@ First, read the @./2025-11-vision.md - [ ] Have variables store a `Referenceable` - [ ] Separate methods and functions - [ ] Add ability to add comments to types, methods/functions and operations, and generate docs from them + - [ ] Clean-up: + - [ ] Move indexing and property access (see e.g.`into_indexed`) etc to the type resolution system - this map allow us to remove + things like `AnyLevelCopyOnWrite` and the `TypeMapper` + - [ ] Most matching methods on `AnyValue` would be better as leaf methods ## Methods and closures diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index dc9f7536..313e7400 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -6,16 +6,16 @@ pub(crate) type DynContent<'a, D, F> = ::DynLeaf<'a, ::DynContent>; /// For types which have an associated value (type and form) -pub(crate) trait IsValueContent<'a> { +pub(crate) trait IsValueContent { type Type: IsHierarchicalType; type Form: IsHierarchicalForm; } -pub(crate) trait FromValueContent<'a>: IsValueContent<'a> { +pub(crate) trait FromValueContent<'a>: IsValueContent { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self; } -pub(crate) trait FromSpannedValueContent<'a>: IsValueContent<'a> { +pub(crate) trait FromSpannedValueContent<'a>: IsValueContent { fn from_spanned_content(content: Spanned>) -> Self; } @@ -26,7 +26,7 @@ impl<'a, C: FromValueContent<'a>> FromSpannedValueContent<'a> for C { } } -impl<'a, C: IsValueContent<'a>> IsValueContent<'a> for Spanned { +impl IsValueContent for Spanned { type Type = C::Type; type Form = C::Form; } @@ -39,7 +39,7 @@ impl<'a, C: FromValueContent<'a>> FromSpannedValueContent<'a> for Spanned { } } -pub(crate) trait IntoValueContent<'a>: IsValueContent<'a> +pub(crate) trait IntoValueContent<'a>: IsValueContent where Self: Sized, { @@ -72,7 +72,7 @@ where fn into_referenceable(self) -> Content<'a, Self::Type, BeReferenceable> where - Self: IsValueContent<'a, Form = BeOwned>, + Self: IsValueContent, { map_via_leaf! { input: (Content<'a, Self::Type, Self::Form>) = self.into_content(), @@ -94,12 +94,11 @@ where description: &str, ) -> ExecutionResult where - >::Type: DowncastFrom, + ::Type: DowncastFrom, { let Spanned(value, span_range) = self; let content = value.into_content(); - let resolved = - <>::Type>::resolve(content, span_range, description)?; + let resolved = <::Type>::resolve(content, span_range, description)?; Ok(X::from_spanned_content(Spanned(resolved, span_range))) } } @@ -117,7 +116,7 @@ where /// Implementations on the value contents *themselves*. /// Typically this is reserved for things operating on references - otherwise we can /// implement on IntoValueContent / FromValueContent. -pub(crate) trait IsSelfValueContent<'a>: IsValueContent<'a> +pub(crate) trait IsSelfValueContent<'a>: IsValueContent where Self::Type: IsHierarchicalType = Self>, Self::Form: IsHierarchicalForm, @@ -191,7 +190,7 @@ where impl<'a, X> IsSelfValueContent<'a> for X where - X: IsValueContent<'a>, + X: IsValueContent, X::Type: IsHierarchicalType = X>, X::Form: IsHierarchicalForm, { diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index 46729e20..f096c4d9 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -1,14 +1,31 @@ use super::*; +impl<'a, L: IsValueLeaf> IsValueContent for AnyMut<'a, L> { + type Type = L::Type; + type Form = BeAnyMut; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for AnyMut<'a, L> { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for AnyMut<'a, L> { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} + #[derive(Copy, Clone)] pub(crate) struct BeAnyMut; impl IsForm for BeAnyMut {} impl IsHierarchicalForm for BeAnyMut { - type Leaf<'a, T: IsLeafType> = crate::internal_prelude::AnyMut<'a, T::Leaf>; + type Leaf<'a, T: IsLeafType> = AnyMut<'a, T::Leaf>; } impl IsDynCompatibleForm for BeAnyMut { - type DynLeaf<'a, D: 'static + ?Sized> = crate::internal_prelude::AnyMut<'a, D>; + type DynLeaf<'a, D: 'static + ?Sized> = AnyMut<'a, D>; } impl IsDynMappableForm for BeAnyMut { diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index f1ca1f2c..bc380b31 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -1,6 +1,21 @@ use super::*; -type QqqAnyRef<'a, T> = Content<'a, T, BeAnyRef>; +impl<'a, L: IsValueLeaf> IsValueContent for AnyRef<'a, L> { + type Type = L::Type; + type Form = BeAnyRef; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for AnyRef<'a, L> { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for AnyRef<'a, L> { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} #[derive(Copy, Clone)] pub(crate) struct BeAnyRef; diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index 280c346d..8bed6110 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -1,13 +1,19 @@ use super::*; -pub(crate) type QqqArgumentValue = Content<'static, T, BeArgument>; - #[derive(Copy, Clone)] pub(crate) struct BeArgument; impl IsForm for BeArgument {} impl IsHierarchicalForm for BeArgument { - type Leaf<'a, T: IsLeafType> = ArgumentContent; + type Leaf<'a, T: IsLeafType> = Argument; +} + +pub(crate) enum Argument { + Owned(T), + CopyOnWrite(QqqCopyOnWrite), + Mutable(QqqMutable), + Assignee(QqqAssignee), + Shared(QqqShared), } impl MapFromArgument for BeArgument { @@ -20,11 +26,3 @@ impl MapFromArgument for BeArgument { // Ok(value) } } - -pub(crate) enum ArgumentContent { - Owned(T), - CopyOnWrite(CopyOnWriteContent), - Mutable(MutableSubRcRefCell), - Assignee(MutableSubRcRefCell), - Shared(SharedSubRcRefCell), -} diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index f8b248b2..503cccdb 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -2,6 +2,23 @@ use super::*; pub(crate) struct QqqAssignee(pub(crate) MutableSubRcRefCell); +impl IsValueContent for QqqAssignee { + type Type = L::Type; + type Form = BeAssignee; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqAssignee { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqAssignee { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} + #[derive(Copy, Clone)] pub(crate) struct BeAssignee; impl IsForm for BeAssignee {} diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 0fce3529..4d8d7225 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -5,93 +5,268 @@ pub(crate) struct BeCopyOnWrite; impl IsForm for BeCopyOnWrite {} impl IsHierarchicalForm for BeCopyOnWrite { - type Leaf<'a, T: IsLeafType> = CopyOnWriteContent; + type Leaf<'a, T: IsLeafType> = QqqCopyOnWrite; } -impl IsDynCompatibleForm for BeCopyOnWrite { - type DynLeaf<'a, D: 'static + ?Sized> = CopyOnWriteContent>; -} +impl BeCopyOnWrite { + pub(crate) fn new_owned<'a, T: IsHierarchicalType>( + owned: Content<'a, T, BeOwned>, + ) -> Content<'a, T, BeCopyOnWrite> { + map_via_leaf! { + input: (Content<'a, T, BeOwned>) = owned, + fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { + T::leaf_to_content(QqqCopyOnWrite::Owned(leaf)) + } + } + } -impl IsDynMappableForm for BeCopyOnWrite { - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, - ) -> Option> - where - T::Leaf: CastDyn, - { - // TODO: Add back once we add a map to CopyOnWriteContent - todo!() + pub(crate) fn new_shared_in_place_of_owned<'a, T: IsHierarchicalType>( + shared: Content<'a, T, BeShared>, + ) -> Content<'a, T, BeCopyOnWrite> { + map_via_leaf! { + input: (Content<'a, T, BeShared>) = shared, + fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { + T::leaf_to_content(QqqCopyOnWrite::SharedWithInfallibleCloning(leaf)) + } + } + } + + pub(crate) fn new_shared_in_place_of_shared<'a, T: IsHierarchicalType>( + shared: Content<'a, T, BeShared>, + ) -> Content<'a, T, BeCopyOnWrite> { + map_via_leaf! { + input: (Content<'a, T, BeShared>) = shared, + fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { + T::leaf_to_content(QqqCopyOnWrite::SharedWithTransparentCloning(leaf)) + } + } } } +pub(crate) enum QqqCopyOnWrite { + /// An owned value that can be used directly + Owned(Owned), + /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). + /// In this case, returning a Cow is just an optimization and we can always clone infallibly. + SharedWithInfallibleCloning(QqqShared), + /// For use when the CopyOnWrite value represents a pre-cloned read-only value. + /// A transparent clone may fail in this case at use time. + SharedWithTransparentCloning(QqqShared), +} + impl MapFromArgument for BeCopyOnWrite { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; fn from_argument_value( _value: ArgumentValue, ) -> ExecutionResult> { - todo!() // value.expect_copy_on_write() + todo!() } } impl LeafAsRefForm for BeCopyOnWrite { fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { match leaf { - CopyOnWriteContent::Owned(owned) => owned, - CopyOnWriteContent::SharedWithInfallibleCloning(shared) => shared, - CopyOnWriteContent::SharedWithTransparentCloning(shared) => shared, + QqqCopyOnWrite::Owned(owned) => owned, + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => shared, + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => shared, } } fn leaf_clone_to_owned_infallible<'r, 'a: 'r, T: IsLeafType>( leaf: &'r Self::Leaf<'a, T>, ) -> T::Leaf { - todo!("Need to copy the custom implementation") + match leaf { + QqqCopyOnWrite::Owned(owned) => owned.clone(), + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => { + BeShared::leaf_clone_to_owned_infallible::(shared) + } + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => { + BeShared::leaf_clone_to_owned_infallible::(shared) + } + } } fn leaf_clone_to_owned_transparently<'r, 'a: 'r, T: IsLeafType>( leaf: &'r Self::Leaf<'a, T>, error_span: SpanRange, ) -> ExecutionResult { - todo!("Need to copy the custom implementation") + match leaf { + QqqCopyOnWrite::Owned(owned) => Ok(owned.clone()), + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => { + Ok(BeShared::leaf_clone_to_owned_infallible::(shared)) + } + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => { + BeShared::leaf_clone_to_owned_transparently::(shared, error_span) + } + } } } -/// Typically O == T or more specifically, ::Owned -pub(crate) enum CopyOnWriteContent { - /// An owned value that can be used directly - Owned(Owned), - /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). - /// In this case, returning a Cow is just an optimization and we can always clone infallibly. - SharedWithInfallibleCloning(Shared), - /// For use when the CopyOnWrite value represents a pre-cloned read-only value. - /// A transparent clone may fail in this case at use time. - SharedWithTransparentCloning(Shared), -} - pub(crate) trait IsSelfCopyOnWriteContent<'a>: IsSelfValueContent<'a> where - Self: IsValueContent<'a, Form = BeCopyOnWrite>, + Self: IsValueContent, Self::Type: IsHierarchicalType = Self>, { + fn into_any_level_copy_on_write(self) -> AnyLevelCopyOnWrite<'a, Self::Type> + where + Self: Sized, + { + map_via_leaf! { + input: (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (AnyLevelCopyOnWrite<'a, T>) { + match leaf { + QqqCopyOnWrite::Owned(owned) => AnyLevelCopyOnWrite::Owned(T::leaf_to_content(owned)), + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => AnyLevelCopyOnWrite::SharedWithInfallibleCloning(T::leaf_to_content(shared)), + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => AnyLevelCopyOnWrite::SharedWithTransparentCloning(T::leaf_to_content(shared)), + } + } + } + } + + fn into_owned_infallible(self) -> Content<'static, Self::Type, BeOwned> + where + Self: Sized, + { + map_via_leaf! { + input: (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { + T::leaf_to_content(match leaf { + QqqCopyOnWrite::Owned(owned) => owned, + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), + }) + } + } + } + + fn into_owned_transparently( + self, + error_span: SpanRange, + ) -> ExecutionResult> + where + Self: Sized, + { + map_via_leaf! { + input: (Content<'a, Self::Type, Self::Form>) = self, + state: SpanRange | let error_span = error_span, + fn map_leaf(leaf) -> (ExecutionResult>) { + Ok(T::leaf_to_content(match leaf { + QqqCopyOnWrite::Owned(owned) => owned, + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => BeShared::leaf_clone_to_owned_transparently::(&shared, error_span)?, + })) + } + } + } + fn acts_as_shared_reference(&self) -> bool { map_via_leaf! { input: &'r (Content<'a, Self::Type, Self::Form>) = self, fn map_leaf(leaf) -> (bool) { match leaf { - CopyOnWriteContent::Owned(_) => false, - CopyOnWriteContent::SharedWithInfallibleCloning(_) => false, - CopyOnWriteContent::SharedWithTransparentCloning(_) => true, + QqqCopyOnWrite::Owned(_) => false, + QqqCopyOnWrite::SharedWithInfallibleCloning(_) => false, + QqqCopyOnWrite::SharedWithTransparentCloning(_) => true, } } } } + + // TODO - Find alternative implementation or replace + // fn map(self, mapper: M) -> ExecutionResult> + // where + // M: TypeMapper, + // Self: Sized, + // { + // Ok(match self.into_any_level_copy_on_write() { + // AnyLevelCopyOnWrite::Owned(owned) => { + // BeCopyOnWrite::new_owned::(mapper.map_owned(owned)?) + // } + // AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { + // let shared_old = shared.clone(); + // let ref_value = shared.as_ref_value(); + // map_via_leaf! { + // input: (Content<'a, Self::Type, BeRef>) = ref_value, + // state: QqqShared | let ref_value = ref_value, + // fn map_leaf(leaf) -> (Content<'a, M::TTo, BeCopyOnWrite>) { + // QqqCopyOnWrite::SharedWithInfallibleCloning(leaf.try_map(|_ignored| { + // QqqCopyOnWrite::SharedWithInfallibleCloning(ref_value) + // })) + // } + // } + // BeCopyOnWrite::new_shared_in_place_of_owned::(mapper.map_ref(shared)?) + // } + // AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { + // BeCopyOnWrite::new_shared_in_place_of_shared::(mapper.map_ref(shared)?) + // } + // }) + // } +} + +/// Using the leaf-based type [`Content<'a, T, BeCopyOnWrite>`] is usually preferred, +/// but in some instances (particularly around supporting old code), we may want the +/// partitoning to be at a higher level (e.g. an `Owned(OwnedValue)` or `Shared(SharedValue)`). +/// +/// That is what this type represents. +pub(crate) enum AnyLevelCopyOnWrite<'a, T: IsHierarchicalType> { + Owned(Content<'a, T, BeOwned>), + SharedWithInfallibleCloning(Content<'a, T, BeShared>), + SharedWithTransparentCloning(Content<'a, T, BeShared>), +} + +impl<'a, T: IsHierarchicalType> AnyLevelCopyOnWrite<'a, T> { + pub fn into_copy_on_write(self) -> Content<'a, T, BeCopyOnWrite> { + match self { + AnyLevelCopyOnWrite::Owned(owned) => BeCopyOnWrite::new_owned::(owned), + AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { + BeCopyOnWrite::new_shared_in_place_of_owned::(shared) + } + AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { + BeCopyOnWrite::new_shared_in_place_of_shared::(shared) + } + } + } +} + +// TODO[concepts]: COPY ON WRITE MAPPING +// How map can work: +// - Create AnyLevelCopyOnWrite<'a, T>: +// * AnyLevelCopyOnWrite::<'a, T>::Owned(Content<'a, T, BeOwned>) +// * AnyLevelCopyOnWrite::<'a, T>::SharedAsX(Content<'a, T, BeShared>) +// - Use a leaf mapper to convert Content<'a, TFrom, BeCopyOnWrite> to AnyLevelCopyOnWrite::<'a, TFrom> +// - Then map the inner Content via TFrom::map_to::() +// - Then convert AnyLevelCopyOnWrite<'a, TTo> back to Content<'a, TTo, BeCopyOnWrite> via leaf mapping `Owned` / `Shared` *to* copy on write (see e.g. as_referencable) +// - Create an AnyValuePropertyAccessor and an AnyValueIndexer +trait TypeMapper { + type TFrom: IsHierarchicalType; + type TTo: IsHierarchicalType; +} +trait OwnedTypeMapper: TypeMapper { + fn map_owned( + self, + owned_value: Content<'static, Self::TFrom, BeOwned>, + ) -> ExecutionResult>; +} + +trait RefTypeMapper: TypeMapper { + fn map_ref<'a>( + self, + ref_value: Content<'a, Self::TFrom, BeRef>, + ) -> ExecutionResult>; +} + +trait MutTypeMapper: TypeMapper { + fn map_mut<'a>( + self, + mut_value: Content<'a, Self::TFrom, BeMut>, + ) -> ExecutionResult>; } impl<'a, C: IsSelfValueContent<'a>> IsSelfCopyOnWriteContent<'a> for C where - Self: IsValueContent<'a, Form = BeCopyOnWrite>, + Self: IsValueContent, Self::Type: IsHierarchicalType = Self>, { } diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index 33ee4140..00c35040 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -10,50 +10,32 @@ use super::*; /// whether the method needs `x[a]` to be a shared reference, mutable reference or an owned value. /// /// So instead, we take the most powerful access we can have for `x[a]`, and convert it later. -pub(crate) type QqqLateBound = Content<'static, T, BeLateBound>; - #[derive(Copy, Clone)] pub(crate) struct BeLateBound; impl IsForm for BeLateBound {} impl IsHierarchicalForm for BeLateBound { - type Leaf<'a, T: IsLeafType> = LateBoundContent; -} - -impl IsDynCompatibleForm for BeLateBound { - type DynLeaf<'a, D: 'static + ?Sized> = LateBoundContent>; -} - -impl IsDynMappableForm for BeLateBound { - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, - ) -> Option> - where - T::Leaf: CastDyn, - { - // TODO: Add back once we add a map to LateBoundContent - todo!() - } + type Leaf<'a, T: IsLeafType> = QqqLateBound; } -pub(crate) enum LateBoundContent { +pub(crate) enum QqqLateBound { /// An owned value that can be converted to any ownership type - Owned(LateBoundOwned), + Owned(QqqLateBoundOwned), /// A copy-on-write value that can be converted to an owned value - CopyOnWrite(CopyOnWriteContent), + CopyOnWrite(QqqCopyOnWrite), /// A mutable reference - Mutable(MutableSubRcRefCell), + Mutable(QqqMutable), /// A shared reference where mutable access failed for a specific reason - Shared(LateBoundShared), + Shared(QqqLateBoundShared), } -pub(crate) struct LateBoundOwned { +pub(crate) struct QqqLateBoundOwned { pub(crate) owned: O, pub(crate) is_from_last_use: bool, } /// A shared value where mutable access failed for a specific reason -pub(crate) struct LateBoundShared { - pub(crate) shared: SharedSubRcRefCell, +pub(crate) struct QqqLateBoundShared { + pub(crate) shared: QqqShared, pub(crate) reason_not_mutable: syn::Error, } diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index e58d7199..64ac801e 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -2,6 +2,23 @@ use super::*; pub(crate) type QqqMutable = MutableSubRcRefCell; +impl IsValueContent for QqqMutable { + type Type = L::Type; + type Form = BeMutable; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqMutable { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqMutable { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} + #[derive(Copy, Clone)] pub(crate) struct BeMutable; impl IsForm for BeMutable {} diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 467b1086..6b693e2c 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -3,6 +3,23 @@ use super::*; /// Just [`T`]! This exists simply to be a name for symmetry with e.g. Shared or Mutable. pub(crate) type Owned = T; +// impl IsValueContent for L { +// type Type = L::Type; +// type Form = BeOwned; +// } + +// impl<'a, L: IsValueLeaf> IntoValueContent<'a> for L { +// fn into_content(self) -> Content<'a, Self::Type, Self::Form> { +// ::leaf_to_content(self) +// } +// } + +// impl<'a, L: IsValueLeaf> FromValueContent<'a> for L { +// fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { +// ::content_to_leaf(content) +// } +// } + /// Represents floating owned values. /// /// If you need span information, wrap with `Spanned`. For example, with `x.y[4]`, this would capture both: @@ -70,7 +87,7 @@ mod test { #[test] fn can_as_ref_owned() { let owned_value = 42u64; - let as_ref: QqqRef = owned_value.as_ref_value(); + let as_ref: Content = owned_value.as_ref_value(); assert_eq!(*as_ref, 42u64); } diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index f92b69c1..49de6841 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -1,6 +1,23 @@ use super::*; -type QqqReferenceable = Content<'static, T, BeReferenceable>; +pub(crate) type Referenceable = Rc>; + +impl IsValueContent for Referenceable { + type Type = L::Type; + type Form = BeReferenceable; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for Referenceable { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for Referenceable { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} /// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. /// This is useful for the content of variables. @@ -12,16 +29,5 @@ pub(crate) struct BeReferenceable; impl IsForm for BeReferenceable {} impl IsHierarchicalForm for BeReferenceable { - type Leaf<'a, T: IsLeafType> = Rc>; -} - -impl MapFromArgument for BeReferenceable { - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - - fn from_argument_value( - _value: ArgumentValue, - ) -> ExecutionResult> { - // Rc::new(RefCell::new(value.expect_owned())) - todo!() - } + type Leaf<'a, T: IsLeafType> = Referenceable; } diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 8b2e2587..11080d1a 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -2,6 +2,23 @@ use super::*; pub(crate) type QqqShared = SharedSubRcRefCell; +impl IsValueContent for QqqShared { + type Type = L::Type; + type Form = BeShared; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqShared { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqShared { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} + #[derive(Copy, Clone)] pub(crate) struct BeShared; impl IsForm for BeShared {} diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index bc5b5589..ef634934 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -1,6 +1,21 @@ use super::*; -pub(crate) type QqqMut<'a, T> = Content<'a, T, BeMut>; +impl IsValueContent for &mut L { + type Type = L::Type; + type Form = BeMut; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for &'a mut L { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for &'a mut L { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} /// It can't be an argument because arguments must be owned in some way; /// so that the drop glue can work properly (because they may come from diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index dc75e2cc..6725e587 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -1,6 +1,21 @@ use super::*; -pub(crate) type QqqRef<'a, T> = Content<'a, T, BeRef>; +impl IsValueContent for &L { + type Type = L::Type; + type Form = BeRef; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for &'a L { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for &'a L { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} /// It can't be an argument because arguments must be owned in some way; /// so that the drop glue can work properly (because they may come from diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index 6d39206b..6aa4a6a1 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -149,6 +149,17 @@ macro_rules! __map_via_leaf_to_parent { ($output:ident -> Result, $err:ty>) => { Ok($t::into_parent($output?)) }; + ($output:ident -> AnyLevelCopyOnWrite<$lt:lifetime, $t:ident>) => { + match $output { + AnyLevelCopyOnWrite::Owned(owned) => AnyLevelCopyOnWrite::Owned($t::into_parent(owned)), + AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { + AnyLevelCopyOnWrite::SharedWithInfallibleCloning($t::into_parent(shared)) + } + AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { + AnyLevelCopyOnWrite::SharedWithTransparentCloning($t::into_parent(shared)) + } + } + }; ($output:ident -> $ty:ty) => { $output }; diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index df6c8dec..e877d94f 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -44,10 +44,18 @@ pub(crate) trait IsHierarchicalType: IsType { } pub(crate) trait IsLeafType: IsHierarchicalType { - type Leaf: IsValueLeaf; + type Leaf: IsValueLeaf; + /// It's expected that leaf === content, but it's hard to make the type system express that + /// bound automatically, so two way conversion functions are a reasonable workaround. fn leaf_to_content<'a, F: IsHierarchicalForm>(leaf: F::Leaf<'a, Self>) -> Self::Content<'a, F>; + /// It's expected that leaf === content, but it's hard to make the type system express that + /// bound automatically, so two way conversion functions are a reasonable workaround. + fn content_to_leaf<'a, F: IsHierarchicalForm>( + content: Self::Content<'a, F>, + ) -> F::Leaf<'a, Self>; + fn leaf_kind() -> Self::LeafKind; } @@ -259,10 +267,26 @@ macro_rules! impl_ancestor_chain_conversions { pub(crate) use impl_ancestor_chain_conversions; pub(crate) trait IsValueLeaf: - 'static + IntoValueContent<'static, Form = BeOwned> + CastDyn + Clone + 'static + + for<'a> IntoValueContent<'a, Form = BeOwned> + + IsValueContent::LeafType> + + IsLeafValueContent + + CastDyn + + Clone { } +pub(crate) trait IsLeafValueContent: IsValueContent { + type LeafType: IsLeafType; +} + +impl IsLeafValueContent for L +where + L::Type: IsLeafType, +{ + type LeafType = L::Type; +} + pub(crate) trait IsDynLeaf: 'static where DynMapper: LeafMapper, @@ -591,6 +615,12 @@ macro_rules! define_leaf_type { leaf } + fn content_to_leaf<'a, F: IsHierarchicalForm>( + leaf: Self::Content<'a, F>, + ) -> F::Leaf<'a, Self> { + leaf + } + fn leaf_kind() -> $kind { $kind } @@ -678,8 +708,11 @@ impl DynMapper { macro_rules! impl_value_content_traits { // For leaf types - implements for all common form wrappers (leaf: $type_def:ty, $content_type:ty) => { + // NB - we can't blanket implement this for all L: IsValueLeaf (unlike all the other forms) + // because it potentially conflicts with the &'a L and &'a mut L blanket implementations. + // BeOwned: content is X - impl<'a> IsValueContent<'a> for $content_type { + impl IsValueContent for $content_type { type Type = $type_def; type Form = BeOwned; } @@ -693,139 +726,11 @@ macro_rules! impl_value_content_traits { content } } - - // BeRef: content is &'a X - impl<'a> IsValueContent<'a> for &'a $content_type { - type Type = $type_def; - type Form = BeRef; - } - impl<'a> IntoValueContent<'a> for &'a $content_type { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for &'a $content_type { - fn from_content(content: Self) -> Self { - content - } - } - - // BeMut: content is &'a mut X - impl<'a> IsValueContent<'a> for &'a mut $content_type { - type Type = $type_def; - type Form = BeMut; - } - impl<'a> IntoValueContent<'a> for &'a mut $content_type { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for &'a mut $content_type { - fn from_content(content: Self) -> Self { - content - } - } - - // BeMutable: content is QqqMutable - impl<'a> IsValueContent<'a> for QqqMutable<$content_type> { - type Type = $type_def; - type Form = BeMutable; - } - impl<'a> IntoValueContent<'a> for QqqMutable<$content_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for QqqMutable<$content_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeShared: content is QqqShared - impl<'a> IsValueContent<'a> for QqqShared<$content_type> { - type Type = $type_def; - type Form = BeShared; - } - impl<'a> IntoValueContent<'a> for QqqShared<$content_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for QqqShared<$content_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeAnyRef: content is AnyRef<'a, X> - impl<'a> IsValueContent<'a> for AnyRef<'a, $content_type> { - type Type = $type_def; - type Form = BeAnyRef; - } - impl<'a> IntoValueContent<'a> for AnyRef<'a, $content_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for AnyRef<'a, $content_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeAnyMut: content is AnyMut<'a, X> - impl<'a> IsValueContent<'a> for AnyMut<'a, $content_type> { - type Type = $type_def; - type Form = BeAnyMut; - } - impl<'a> IntoValueContent<'a> for AnyMut<'a, $content_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for AnyMut<'a, $content_type> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeReferenceable: content is Rc> - impl<'a> IsValueContent<'a> for Rc> { - type Type = $type_def; - type Form = BeReferenceable; - } - impl<'a> IntoValueContent<'a> for Rc> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for Rc> { - fn from_content(content: Self) -> Self { - content - } - } - - // BeAssignee: content is QqqAssignee - impl<'a> IsValueContent<'a> for QqqAssignee<$content_type> { - type Type = $type_def; - type Form = BeAssignee; - } - impl<'a> IntoValueContent<'a> for QqqAssignee<$content_type> { - fn into_content(self) -> Self { - self - } - } - impl<'a> FromValueContent<'a> for QqqAssignee<$content_type> { - fn from_content(content: Self) -> Self { - content - } - } }; // For parent types - $content<'a, F> where F: IsHierarchicalForm (parent: $type_def:ty, $content:ident) => { - impl<'a, F: IsHierarchicalForm> IsValueContent<'a> for $content<'a, F> { + impl<'a, F: IsHierarchicalForm> IsValueContent for $content<'a, F> { type Type = $type_def; type Form = F; } diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index f620526f..c7d6b137 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -21,7 +21,7 @@ define_leaf_type! { }, } -impl<'a> IsValueContent<'a> for &'a str { +impl IsValueContent for &str { type Type = StringType; type Form = BeRef; } From 6dedf697f271bd0a4593bbe5933d0b4c1709db63 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 15 Jan 2026 02:59:08 +0000 Subject: [PATCH 48/58] refactor: Content associated types have useful trait bounds --- src/expressions/concepts/form.rs | 5 +- src/expressions/concepts/forms/argument.rs | 33 ++++++++--- .../concepts/forms/copy_on_write.rs | 56 ++++++++++++------- src/expressions/concepts/forms/late_bound.rs | 39 +++++++++---- src/expressions/concepts/type_traits.rs | 10 ++-- 5 files changed, 99 insertions(+), 44 deletions(-) diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index de6ba2a1..cef7edf1 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -15,8 +15,9 @@ pub(crate) trait IsForm: Sized + Clone {} pub(crate) trait IsHierarchicalForm: IsForm { /// The standard leaf for a hierachical type - /// Usually this will implement IsValueContent<'a, Type = T, Form = Self> - type Leaf<'a, T: IsLeafType>; + type Leaf<'a, T: IsLeafType>: IsValueContent + + IntoValueContent<'a> + + FromValueContent<'a>; } pub(crate) trait LeafAsRefForm: IsHierarchicalForm { diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index 8bed6110..c3215aa0 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -1,5 +1,30 @@ use super::*; +pub(crate) enum Argument { + Owned(L), + CopyOnWrite(QqqCopyOnWrite), + Mutable(QqqMutable), + Assignee(QqqAssignee), + Shared(QqqShared), +} + +impl IsValueContent for Argument { + type Type = L::Type; + type Form = BeArgument; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for Argument { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for Argument { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} + #[derive(Copy, Clone)] pub(crate) struct BeArgument; impl IsForm for BeArgument {} @@ -8,14 +33,6 @@ impl IsHierarchicalForm for BeArgument { type Leaf<'a, T: IsLeafType> = Argument; } -pub(crate) enum Argument { - Owned(T), - CopyOnWrite(QqqCopyOnWrite), - Mutable(QqqMutable), - Assignee(QqqAssignee), - Shared(QqqShared), -} - impl MapFromArgument for BeArgument { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 4d8d7225..58e46611 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -1,5 +1,33 @@ use super::*; +pub(crate) enum QqqCopyOnWrite { + /// An owned value that can be used directly + Owned(Owned), + /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). + /// In this case, returning a Cow is just an optimization and we can always clone infallibly. + SharedWithInfallibleCloning(QqqShared), + /// For use when the CopyOnWrite value represents a pre-cloned read-only value. + /// A transparent clone may fail in this case at use time. + SharedWithTransparentCloning(QqqShared), +} + +impl IsValueContent for QqqCopyOnWrite { + type Type = L::Type; + type Form = BeCopyOnWrite; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqCopyOnWrite { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqCopyOnWrite { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} + #[derive(Copy, Clone)] pub(crate) struct BeCopyOnWrite; impl IsForm for BeCopyOnWrite {} @@ -43,17 +71,6 @@ impl BeCopyOnWrite { } } -pub(crate) enum QqqCopyOnWrite { - /// An owned value that can be used directly - Owned(Owned), - /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). - /// In this case, returning a Cow is just an optimization and we can always clone infallibly. - SharedWithInfallibleCloning(QqqShared), - /// For use when the CopyOnWrite value represents a pre-cloned read-only value. - /// A transparent clone may fail in this case at use time. - SharedWithTransparentCloning(QqqShared), -} - impl MapFromArgument for BeCopyOnWrite { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; @@ -200,6 +217,7 @@ where // } // AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { // BeCopyOnWrite::new_shared_in_place_of_shared::(mapper.map_ref(shared)?) + // todo!() // } // }) // } @@ -207,7 +225,7 @@ where /// Using the leaf-based type [`Content<'a, T, BeCopyOnWrite>`] is usually preferred, /// but in some instances (particularly around supporting old code), we may want the -/// partitoning to be at a higher level (e.g. an `Owned(OwnedValue)` or `Shared(SharedValue)`). +/// partitioning to be at a higher level (e.g. an `Owned(OwnedValue)` or `Shared(SharedValue)`). /// /// That is what this type represents. pub(crate) enum AnyLevelCopyOnWrite<'a, T: IsHierarchicalType> { @@ -239,25 +257,25 @@ impl<'a, T: IsHierarchicalType> AnyLevelCopyOnWrite<'a, T> { // - Then map the inner Content via TFrom::map_to::() // - Then convert AnyLevelCopyOnWrite<'a, TTo> back to Content<'a, TTo, BeCopyOnWrite> via leaf mapping `Owned` / `Shared` *to* copy on write (see e.g. as_referencable) // - Create an AnyValuePropertyAccessor and an AnyValueIndexer -trait TypeMapper { +pub(crate) trait TypeMapper { type TFrom: IsHierarchicalType; type TTo: IsHierarchicalType; } -trait OwnedTypeMapper: TypeMapper { - fn map_owned( +pub(crate) trait OwnedTypeMapper: TypeMapper { + fn map_owned<'a>( self, - owned_value: Content<'static, Self::TFrom, BeOwned>, - ) -> ExecutionResult>; + owned_value: Content<'a, Self::TFrom, BeOwned>, + ) -> ExecutionResult>; } -trait RefTypeMapper: TypeMapper { +pub(crate) trait RefTypeMapper: TypeMapper { fn map_ref<'a>( self, ref_value: Content<'a, Self::TFrom, BeRef>, ) -> ExecutionResult>; } -trait MutTypeMapper: TypeMapper { +pub(crate) trait MutTypeMapper: TypeMapper { fn map_mut<'a>( self, mut_value: Content<'a, Self::TFrom, BeMut>, diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index 00c35040..4eaa2145 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -1,5 +1,33 @@ use super::*; +pub(crate) enum QqqLateBound { + /// An owned value that can be converted to any ownership type + Owned(QqqLateBoundOwned), + /// A copy-on-write value that can be converted to an owned value + CopyOnWrite(QqqCopyOnWrite), + /// A mutable reference + Mutable(QqqMutable), + /// A shared reference where mutable access failed for a specific reason + Shared(QqqLateBoundShared), +} + +impl IsValueContent for QqqLateBound { + type Type = L::Type; + type Form = BeLateBound; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqLateBound { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + ::leaf_to_content(self) + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqLateBound { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + ::content_to_leaf(content) + } +} + /// Universal value type that can resolve to any concrete ownership type. /// /// Sometimes, a value can be accessed, but we don't yet know *how* we need to access it. @@ -18,17 +46,6 @@ impl IsHierarchicalForm for BeLateBound { type Leaf<'a, T: IsLeafType> = QqqLateBound; } -pub(crate) enum QqqLateBound { - /// An owned value that can be converted to any ownership type - Owned(QqqLateBoundOwned), - /// A copy-on-write value that can be converted to an owned value - CopyOnWrite(QqqCopyOnWrite), - /// A mutable reference - Mutable(QqqMutable), - /// A shared reference where mutable access failed for a specific reason - Shared(QqqLateBoundShared), -} - pub(crate) struct QqqLateBoundOwned { pub(crate) owned: O, pub(crate) is_from_last_use: bool, diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index e877d94f..1bd27ab6 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -19,8 +19,9 @@ pub(crate) trait IsType: Sized { } pub(crate) trait IsHierarchicalType: IsType { - /// Typically this will implement `IsValueContent<'a, Type = Self, Form = F>` - type Content<'a, F: IsHierarchicalForm>; + type Content<'a, F: IsHierarchicalForm>: IsValueContent + + IntoValueContent<'a> + + FromValueContent<'a>; type LeafKind: IsLeafKind; fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( @@ -268,8 +269,9 @@ pub(crate) use impl_ancestor_chain_conversions; pub(crate) trait IsValueLeaf: 'static - + for<'a> IntoValueContent<'a, Form = BeOwned> - + IsValueContent::LeafType> + + IsValueContent::LeafType, Form = BeOwned> + + for<'a> IntoValueContent<'a> + + for<'a> FromValueContent<'a> + IsLeafValueContent + CastDyn + Clone From 3b469b9b3e0e9314c522e36d6f9860c99b570b0c Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 15 Jan 2026 03:46:08 +0000 Subject: [PATCH 49/58] refactor: Remove the need for e.g. content_to_leaf methods --- src/expressions/concepts/content.rs | 10 ++-- src/expressions/concepts/forms/any_mut.rs | 4 +- src/expressions/concepts/forms/any_ref.rs | 4 +- src/expressions/concepts/forms/argument.rs | 4 +- src/expressions/concepts/forms/assignee.rs | 4 +- .../concepts/forms/copy_on_write.rs | 24 ++++----- src/expressions/concepts/forms/late_bound.rs | 4 +- src/expressions/concepts/forms/mutable.rs | 4 +- src/expressions/concepts/forms/owned.rs | 19 ++----- .../concepts/forms/referenceable.rs | 4 +- src/expressions/concepts/forms/shared.rs | 4 +- src/expressions/concepts/forms/simple_mut.rs | 4 +- src/expressions/concepts/forms/simple_ref.rs | 4 +- src/expressions/concepts/mapping.rs | 2 +- src/expressions/concepts/type_traits.rs | 49 ++++++++++--------- 15 files changed, 67 insertions(+), 77 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 313e7400..15570a89 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -77,7 +77,7 @@ where map_via_leaf! { input: (Content<'a, Self::Type, Self::Form>) = self.into_content(), fn map_leaf(leaf) -> (Content<'a, T, BeReferenceable>) { - T::leaf_to_content(Rc::new(RefCell::new(leaf))) + Rc::new(RefCell::new(leaf)) } } } @@ -129,7 +129,7 @@ where map_via_leaf! { input: &'r mut (Content<'a, Self::Type, Self::Form>) = self, fn map_leaf(leaf) -> (Content<'r, T, BeMut>) { - T::leaf_to_content(F::leaf_as_mut(leaf)) + F::leaf_as_mut(leaf) } } } @@ -142,7 +142,7 @@ where map_via_leaf! { input: &'r (Content<'a, Self::Type, Self::Form>) = self, fn map_leaf(leaf) -> (Content<'r, T, BeRef>) { - T::leaf_to_content(F::leaf_as_ref(leaf)) + F::leaf_as_ref(leaf) } } } @@ -159,7 +159,7 @@ where map_via_leaf! { input: &'r (Content<'a, Self::Type, Self::Form>) = self, fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { - T::leaf_to_content(F::leaf_clone_to_owned_infallible(leaf)) + F::leaf_clone_to_owned_infallible(leaf) } } } @@ -182,7 +182,7 @@ where input: &'r (Content<'a, Self::Type, Self::Form>) = self, state: SpanRange | let span_range = span_range, fn map_leaf(leaf) -> (ExecutionResult>) { - Ok(T::leaf_to_content(F::leaf_clone_to_owned_transparently(leaf, span_range)?)) + F::leaf_clone_to_owned_transparently(leaf, span_range) } } } diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index f096c4d9..b05f78de 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -7,13 +7,13 @@ impl<'a, L: IsValueLeaf> IsValueContent for AnyMut<'a, L> { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for AnyMut<'a, L> { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for AnyMut<'a, L> { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index bc380b31..3c694a34 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -7,13 +7,13 @@ impl<'a, L: IsValueLeaf> IsValueContent for AnyRef<'a, L> { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for AnyRef<'a, L> { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for AnyRef<'a, L> { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index c3215aa0..7fbe82f1 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -15,13 +15,13 @@ impl IsValueContent for Argument { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for Argument { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for Argument { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 503cccdb..8c6a2cc7 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -9,13 +9,13 @@ impl IsValueContent for QqqAssignee { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqAssignee { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqAssignee { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 58e46611..a9b37045 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -18,13 +18,13 @@ impl IsValueContent for QqqCopyOnWrite { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqCopyOnWrite { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqCopyOnWrite { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } @@ -43,7 +43,7 @@ impl BeCopyOnWrite { map_via_leaf! { input: (Content<'a, T, BeOwned>) = owned, fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { - T::leaf_to_content(QqqCopyOnWrite::Owned(leaf)) + QqqCopyOnWrite::Owned(leaf) } } } @@ -54,7 +54,7 @@ impl BeCopyOnWrite { map_via_leaf! { input: (Content<'a, T, BeShared>) = shared, fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { - T::leaf_to_content(QqqCopyOnWrite::SharedWithInfallibleCloning(leaf)) + QqqCopyOnWrite::SharedWithInfallibleCloning(leaf) } } } @@ -65,7 +65,7 @@ impl BeCopyOnWrite { map_via_leaf! { input: (Content<'a, T, BeShared>) = shared, fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { - T::leaf_to_content(QqqCopyOnWrite::SharedWithTransparentCloning(leaf)) + QqqCopyOnWrite::SharedWithTransparentCloning(leaf) } } } @@ -134,9 +134,9 @@ where input: (Content<'a, Self::Type, Self::Form>) = self, fn map_leaf(leaf) -> (AnyLevelCopyOnWrite<'a, T>) { match leaf { - QqqCopyOnWrite::Owned(owned) => AnyLevelCopyOnWrite::Owned(T::leaf_to_content(owned)), - QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => AnyLevelCopyOnWrite::SharedWithInfallibleCloning(T::leaf_to_content(shared)), - QqqCopyOnWrite::SharedWithTransparentCloning(shared) => AnyLevelCopyOnWrite::SharedWithTransparentCloning(T::leaf_to_content(shared)), + QqqCopyOnWrite::Owned(owned) => AnyLevelCopyOnWrite::Owned(owned), + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared), + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared), } } } @@ -149,11 +149,11 @@ where map_via_leaf! { input: (Content<'a, Self::Type, Self::Form>) = self, fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { - T::leaf_to_content(match leaf { + match leaf { QqqCopyOnWrite::Owned(owned) => owned, QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), QqqCopyOnWrite::SharedWithTransparentCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), - }) + } } } } @@ -169,11 +169,11 @@ where input: (Content<'a, Self::Type, Self::Form>) = self, state: SpanRange | let error_span = error_span, fn map_leaf(leaf) -> (ExecutionResult>) { - Ok(T::leaf_to_content(match leaf { + Ok(match leaf { QqqCopyOnWrite::Owned(owned) => owned, QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), QqqCopyOnWrite::SharedWithTransparentCloning(shared) => BeShared::leaf_clone_to_owned_transparently::(&shared, error_span)?, - })) + }) } } } diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index 4eaa2145..0395e1ca 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -18,13 +18,13 @@ impl IsValueContent for QqqLateBound { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqLateBound { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqLateBound { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index 64ac801e..f22763ce 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -9,13 +9,13 @@ impl IsValueContent for QqqMutable { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqMutable { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqMutable { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 6b693e2c..46047dde 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -3,22 +3,9 @@ use super::*; /// Just [`T`]! This exists simply to be a name for symmetry with e.g. Shared or Mutable. pub(crate) type Owned = T; -// impl IsValueContent for L { -// type Type = L::Type; -// type Form = BeOwned; -// } - -// impl<'a, L: IsValueLeaf> IntoValueContent<'a> for L { -// fn into_content(self) -> Content<'a, Self::Type, Self::Form> { -// ::leaf_to_content(self) -// } -// } - -// impl<'a, L: IsValueLeaf> FromValueContent<'a> for L { -// fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { -// ::content_to_leaf(content) -// } -// } +// NOTE: Attempting to blanket implement IsValueContent etc for all T: IsValueLeaf causes +// a clash (inlike the other forms) - so instead we work around this with (a) manual impls +// in impl_value_content_traits for each L, and (b) bounds on L: IsValueLeaf. /// Represents floating owned values. /// diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs index 49de6841..47b7b622 100644 --- a/src/expressions/concepts/forms/referenceable.rs +++ b/src/expressions/concepts/forms/referenceable.rs @@ -9,13 +9,13 @@ impl IsValueContent for Referenceable { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for Referenceable { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for Referenceable { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 11080d1a..847ae37b 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -9,13 +9,13 @@ impl IsValueContent for QqqShared { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqShared { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqShared { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index ef634934..f0537f94 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -7,13 +7,13 @@ impl IsValueContent for &mut L { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for &'a mut L { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for &'a mut L { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 6725e587..9bb5672e 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -7,13 +7,13 @@ impl IsValueContent for &L { impl<'a, L: IsValueLeaf> IntoValueContent<'a> for &'a L { fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - ::leaf_to_content(self) + self } } impl<'a, L: IsValueLeaf> FromValueContent<'a> for &'a L { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - ::content_to_leaf(content) + content } } diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index 6aa4a6a1..def960ad 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -180,7 +180,7 @@ mod test_macro { let owned = map_via_leaf! { input: &'r mut (Content<'a, U8Type, BeOwned>) = &mut x, fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { - T::leaf_to_content(F::leaf_clone_to_owned_infallible(leaf)) + F::leaf_clone_to_owned_infallible(leaf) } }; assert_eq!(owned, 4); diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 1bd27ab6..fe0792a1 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -44,19 +44,27 @@ pub(crate) trait IsHierarchicalType: IsType { ) -> Self::LeafKind; } -pub(crate) trait IsLeafType: IsHierarchicalType { +pub(crate) trait IsLeafType: + IsHierarchicalType + // NOTE: We can't create a universal bound over F: IsHierarchicalForm in rust + // If you need a generic interconversion over all such F, we'll need to reintroduce + // e.g. a fn content_to_leaf(content) method, with body { self } in each impl. + // For now though, listing out all the forms here is sufficient. + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> +{ type Leaf: IsValueLeaf; - /// It's expected that leaf === content, but it's hard to make the type system express that - /// bound automatically, so two way conversion functions are a reasonable workaround. - fn leaf_to_content<'a, F: IsHierarchicalForm>(leaf: F::Leaf<'a, Self>) -> Self::Content<'a, F>; - - /// It's expected that leaf === content, but it's hard to make the type system express that - /// bound automatically, so two way conversion functions are a reasonable workaround. - fn content_to_leaf<'a, F: IsHierarchicalForm>( - content: Self::Content<'a, F>, - ) -> F::Leaf<'a, Self>; - fn leaf_kind() -> Self::LeafKind; } @@ -269,6 +277,13 @@ pub(crate) use impl_ancestor_chain_conversions; pub(crate) trait IsValueLeaf: 'static + // This whole creation of IsLeafValueContent and the LeafType bound is a workaround to + // add an implied bound to IsValueLeaf that Self::Type: IsLeafType + // - This "use an associated type equality constraint" workaround came from a rust thread + // related to implied bounds, which of course I can't find now. + // - The main caveat is that order matters (i.e. I had to set Type == LeafType) and bound the + // correct one of Type or LeafType in other places to avoid circularity and ensure the one way + // resolution logic works correctly. + IsValueContent::LeafType, Form = BeOwned> + for<'a> IntoValueContent<'a> + for<'a> FromValueContent<'a> @@ -611,18 +626,6 @@ macro_rules! define_leaf_type { impl IsLeafType for $type_def { type Leaf = $content_type; - fn leaf_to_content<'a, F: IsHierarchicalForm>( - leaf: F::Leaf<'a, Self>, - ) -> Self::Content<'a, F> { - leaf - } - - fn content_to_leaf<'a, F: IsHierarchicalForm>( - leaf: Self::Content<'a, F>, - ) -> F::Leaf<'a, Self> { - leaf - } - fn leaf_kind() -> $kind { $kind } From 5edc0e7ac2f88edffa455a5415327ef61ba8e1cc Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 17 Jan 2026 16:52:24 +0000 Subject: [PATCH 50/58] feat: First impl of map for new CopyOnWrite --- .../concepts/forms/copy_on_write.rs | 88 +++++++++++-------- src/expressions/concepts/mapping.rs | 18 ++-- src/expressions/concepts/type_traits.rs | 4 + src/misc/mut_rc_ref_cell.rs | 34 ++++++- 4 files changed, 99 insertions(+), 45 deletions(-) diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index a9b37045..25aa39d3 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -1,14 +1,14 @@ use super::*; -pub(crate) enum QqqCopyOnWrite { +pub(crate) enum QqqCopyOnWrite { /// An owned value that can be used directly - Owned(Owned), + Owned(Owned), /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). /// In this case, returning a Cow is just an optimization and we can always clone infallibly. - SharedWithInfallibleCloning(QqqShared), + SharedWithInfallibleCloning(QqqShared), /// For use when the CopyOnWrite value represents a pre-cloned read-only value. /// A transparent clone may fail in this case at use time. - SharedWithTransparentCloning(QqqShared), + SharedWithTransparentCloning(QqqShared), } impl IsValueContent for QqqCopyOnWrite { @@ -192,35 +192,53 @@ where } // TODO - Find alternative implementation or replace - // fn map(self, mapper: M) -> ExecutionResult> - // where - // M: TypeMapper, - // Self: Sized, - // { - // Ok(match self.into_any_level_copy_on_write() { - // AnyLevelCopyOnWrite::Owned(owned) => { - // BeCopyOnWrite::new_owned::(mapper.map_owned(owned)?) - // } - // AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { - // let shared_old = shared.clone(); - // let ref_value = shared.as_ref_value(); - // map_via_leaf! { - // input: (Content<'a, Self::Type, BeRef>) = ref_value, - // state: QqqShared | let ref_value = ref_value, - // fn map_leaf(leaf) -> (Content<'a, M::TTo, BeCopyOnWrite>) { - // QqqCopyOnWrite::SharedWithInfallibleCloning(leaf.try_map(|_ignored| { - // QqqCopyOnWrite::SharedWithInfallibleCloning(ref_value) - // })) - // } - // } - // BeCopyOnWrite::new_shared_in_place_of_owned::(mapper.map_ref(shared)?) - // } - // AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { - // BeCopyOnWrite::new_shared_in_place_of_shared::(mapper.map_ref(shared)?) - // todo!() - // } - // }) - // } + fn map(self, mapper: M) -> ExecutionResult> + where + 'a: 'static, + M: TypeMapper, // u32 is temporary to see if we can make it work without adding generics to `map_via_leaf!` + Self: Sized, + Self: IsValueContent, // Temporary to see if we can make it work without adding generics to `map_via_leaf!` + { + + Ok(match self.into_any_level_copy_on_write() { + AnyLevelCopyOnWrite::Owned(owned) => { + BeCopyOnWrite::new_owned::(mapper.map_owned(owned)?) + } + AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { + let shared = map_via_leaf! { + input: (Content<'a, Self::Type, BeShared>) = shared, + fn map_leaf(leaf) -> (ExecutionResult>>) { + leaf.try_map(|value_ref| { + let from_ref = value_ref.into_any(); + inner_map_ref(from_ref) // &Content + }) + } + }?; + struct AsIs(T); + shared.replace(|content, encapsulator| { + map_via_leaf! { + input: &'r (Content<'a, AnyType, BeOwned>) = content, + state: | <'r2> Encapsulator<'r2, AnyValue, AnyValue> | let encapsulator = encapsulator, + fn map_leaf(leaf) -> (AsIs>) { + let shared_leaf = encapsulator.encapsulate(leaf); + let new_leaf = QqqCopyOnWrite::SharedWithInfallibleCloning(shared_leaf); + AsIs(new_leaf.into_any()) + } + } + }).0 + } + AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { + todo!() + } + }) + } +} + + +fn inner_map_ref<'a>( + ref_value: Content<'a, AnyType, BeRef>, +) -> ExecutionResult<&'a Content<'static, AnyType, BeOwned>> { + unimplemented!() } /// Using the leaf-based type [`Content<'a, T, BeCopyOnWrite>`] is usually preferred, @@ -272,14 +290,14 @@ pub(crate) trait RefTypeMapper: TypeMapper { fn map_ref<'a>( self, ref_value: Content<'a, Self::TFrom, BeRef>, - ) -> ExecutionResult>; + ) -> ExecutionResult<&'a Content<'a, Self::TTo, BeOwned>>; } pub(crate) trait MutTypeMapper: TypeMapper { fn map_mut<'a>( self, mut_value: Content<'a, Self::TFrom, BeMut>, - ) -> ExecutionResult>; + ) -> ExecutionResult<&'a mut Content<'a, Self::TTo, BeOwned>>; } impl<'a, C: IsSelfValueContent<'a>> IsSelfCopyOnWriteContent<'a> for C diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index def960ad..c473a58c 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -62,17 +62,17 @@ pub(crate) trait MutLeafMapper { macro_rules! map_via_leaf { ( input: $(&$r:lifetime $($mut:ident)?)? (Content<$a:lifetime, $input_type:ty, $input_form:ty>) = $input_value:expr, - $(state: $state_type:ty | let $state_pat:pat = $state_init:expr,)? + $(state: $(| <$state_l:lifetime>)? $state_type:ty | let $state_pat:pat = $state_init:expr,)? fn map_leaf <$f:ident $(: $fb:ident $(+ $fbe:ident )*)? $(= $fixed_form:ty)?, $t:ident> ($leaf:ident) -> ($($output_type:tt)+) $(where $($where_clause:tt)*)? $body:block ) => {{ - struct __InlineMapper { + struct __InlineMapper $($(<$state_l>)?)? { state: $crate::if_exists!{ {$($state_type)?} {$($state_type)?} {()} }, } $crate::__map_via_leaf_trait_impl!{ - $(@fixed_form $fixed_form |)? impl<$f $(: $fb $(+ $fbe)*)?> @mutability $(&$r $($mut)?)? for __InlineMapper $(where $($where_clause)*)? + $(@fixed_form $fixed_form |)? impl<$f $(: $fb $(+ $fbe)*)?> @mutability $(&$r $($mut)?)? for __InlineMapper $($(<$state_l>)?)? $(where $($where_clause)*)? { type Output<$($r,)? $a $(: $r)?, $t: $crate::expressions::concepts::IsHierarchicalType> = $($output_type)+; @@ -107,12 +107,12 @@ macro_rules! map_via_leaf { #[doc(hidden)] macro_rules! __map_via_leaf_trait_impl { - (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime mut for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl <$f $(: $fb $(+ $fbe)*)?> MutLeafMapper<$f> for $mapper $(where $($where_clause)*)? { $($body)* } }; - (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl <$f $(: $fb $(+ $fbe)*)?> RefLeafMapper<$f> for $mapper $(where $($where_clause)*)? { $($body)* } }; - (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl <$f $(: $fb $(+ $fbe)*)?> LeafMapper<$f> for $mapper $(where $($where_clause)*)? { $($body)* } }; - (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime mut for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl MutLeafMapper<$fixed_form> for $mapper $(where $($where_clause)*)? { $($body)* } }; - (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl RefLeafMapper<$fixed_form> for $mapper $(where $($where_clause)*)? { $($body)* } }; - (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability for $mapper:ident $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl LeafMapper<$fixed_form> for $mapper $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime mut for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? <$f $(: $fb $(+ $fbe)*)?> MutLeafMapper<$f> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? <$f $(: $fb $(+ $fbe)*)?> RefLeafMapper<$f> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? <$f $(: $fb $(+ $fbe)*)?> LeafMapper<$f> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime mut for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? MutLeafMapper<$fixed_form> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? RefLeafMapper<$fixed_form> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? LeafMapper<$fixed_form> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; } #[doc(hidden)] diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index fe0792a1..4b301dd7 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -62,6 +62,10 @@ pub(crate) trait IsLeafType: + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + UpcastTo + + UpcastTo + + UpcastTo + + UpcastTo { type Leaf: IsValueLeaf; diff --git a/src/misc/mut_rc_ref_cell.rs b/src/misc/mut_rc_ref_cell.rs index 74e10e7f..d978afd3 100644 --- a/src/misc/mut_rc_ref_cell.rs +++ b/src/misc/mut_rc_ref_cell.rs @@ -162,7 +162,7 @@ impl SharedSubRcRefCell { } } - pub(crate) fn map(self, f: impl FnOnce(&U) -> &V) -> SharedSubRcRefCell { + pub(crate) fn map(self, f: impl for<'a> FnOnce(&'a U) -> &'a V) -> SharedSubRcRefCell { SharedSubRcRefCell { shared_ref: Ref::map(self.shared_ref, f), pointed_at: self.pointed_at, @@ -200,6 +200,19 @@ impl SharedSubRcRefCell { } } + pub(crate) fn replace( + self, + f: impl for<'a> FnOnce(&'a U, Encapsulator<'a, T, U>) -> O, + ) -> O { + f( + &*Ref::clone(&self.shared_ref), + Encapsulator { + inner: self, + encapsulation_lifetime: std::marker::PhantomData, + } + ) + } + /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -226,6 +239,25 @@ impl SharedSubRcRefCell { } } + +pub(crate) struct Encapsulator<'a, T: ?Sized, U: 'static + ?Sized> { + inner: SharedSubRcRefCell, + encapsulation_lifetime: std::marker::PhantomData<&'a ()>, +} + +impl <'a, T: 'static + ?Sized, U: 'static + ?Sized> Encapsulator<'a, T, U> { + pub(crate) fn encapsulate( + self, + value: &'a V, + ) -> SharedSubRcRefCell { + self.inner.map(|_| + // SAFETY: The lifetime 'a is equal to the &'a content argument in replace + // So this guarantees that the returned reference is valid as long as the SharedSubRcRefCell exists + unsafe { less_buggy_transmute::<&'a V, &'static V>(value) } + ) + } +} + impl Deref for SharedSubRcRefCell { type Target = U; From 5eff453ef746c17bd6f6464439f3a182d878a1d1 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 17 Jan 2026 16:52:38 +0000 Subject: [PATCH 51/58] refactor: Tweaks to map impl --- .../concepts/forms/copy_on_write.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 25aa39d3..5f81d11c 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -205,27 +205,30 @@ where BeCopyOnWrite::new_owned::(mapper.map_owned(owned)?) } AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { + // Apply map, get Shared> let shared = map_via_leaf! { input: (Content<'a, Self::Type, BeShared>) = shared, fn map_leaf(leaf) -> (ExecutionResult>>) { leaf.try_map(|value_ref| { let from_ref = value_ref.into_any(); - inner_map_ref(from_ref) // &Content + inner_map_ref(from_ref) // &Content + + // If inner_map_ref returned a Content<'a, M::TTo, BeRef> + // then we'd need to move the leaf map inside here, but it'd work the same }) } }?; - struct AsIs(T); - shared.replace(|content, encapsulator| { + // Migrate Shared into leaf + let shared_content = shared.replace(|content, encapsulator| { map_via_leaf! { input: &'r (Content<'a, AnyType, BeOwned>) = content, state: | <'r2> Encapsulator<'r2, AnyValue, AnyValue> | let encapsulator = encapsulator, - fn map_leaf(leaf) -> (AsIs>) { - let shared_leaf = encapsulator.encapsulate(leaf); - let new_leaf = QqqCopyOnWrite::SharedWithInfallibleCloning(shared_leaf); - AsIs(new_leaf.into_any()) + fn map_leaf(leaf) -> (Content<'static, T, BeShared>) { + encapsulator.encapsulate(leaf) } } - }).0 + }); + BeCopyOnWrite::new_shared_in_place_of_owned::(shared_content) } AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { todo!() From d1e0134a9df534bdcc4d2e389a5fd8e69715200d Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 17 Jan 2026 17:02:39 +0000 Subject: [PATCH 52/58] refactor: Upcast and Downcast are form independent --- src/expressions/concepts/content.rs | 16 ++--- src/expressions/concepts/form.rs | 4 +- src/expressions/concepts/forms/any_mut.rs | 2 - src/expressions/concepts/forms/any_ref.rs | 2 - src/expressions/concepts/forms/assignee.rs | 2 - src/expressions/concepts/forms/mutable.rs | 2 - src/expressions/concepts/forms/owned.rs | 2 - src/expressions/concepts/forms/shared.rs | 2 - src/expressions/concepts/forms/simple_mut.rs | 2 - src/expressions/concepts/forms/simple_ref.rs | 2 - src/expressions/concepts/type_traits.rs | 62 +++++++++----------- 11 files changed, 37 insertions(+), 61 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 15570a89..406697ef 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -48,24 +48,24 @@ where #[inline] fn upcast(self) -> Content<'a, S, Self::Form> where - Self::Type: UpcastTo, + Self::Type: UpcastTo, { - ::upcast_to(self.into_content()) + ::upcast_to::(self.into_content()) } #[inline] #[allow(clippy::type_complexity)] // It's actually pretty readable fn downcast(self) -> Result, Content<'a, Self::Type, Self::Form>> where - U: DowncastFrom, + U: DowncastFrom, { - U::downcast_from(self.into_content()) + U::downcast_from::(self.into_content()) } #[inline] fn into_any(self) -> Content<'a, AnyType, Self::Form> where - Self::Type: UpcastTo, + Self::Type: UpcastTo, { self.upcast() } @@ -94,11 +94,11 @@ where description: &str, ) -> ExecutionResult where - ::Type: DowncastFrom, + ::Type: DowncastFrom, { let Spanned(value, span_range) = self; let content = value.into_content(); - let resolved = <::Type>::resolve(content, span_range, description)?; + let resolved = <::Type>::resolve::(content, span_range, description)?; Ok(X::from_spanned_content(Spanned(resolved, span_range))) } } @@ -106,7 +106,7 @@ where // TODO[concepts]: Remove eventually, along with IntoValue impl impl> IntoAnyValue for X where - X::Type: UpcastTo, + X::Type: UpcastTo, { fn into_any_value(self) -> AnyValue { self.into_any() diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index cef7edf1..7fd84f49 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -49,14 +49,12 @@ pub(crate) trait LeafAsMutForm: IsHierarchicalForm { fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf; } -pub(crate) trait IsDynCompatibleForm: IsForm { +pub(crate) trait IsDynCompatibleForm: IsHierarchicalForm { /// The container for a dyn Trait based type. /// The DynLeaf can be similar to the standard leaf, but must be /// able to support an unsized D. type DynLeaf<'a, D: 'static + ?Sized>; -} -pub(crate) trait IsDynMappableForm: IsHierarchicalForm + IsDynCompatibleForm { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index b05f78de..0ad24af9 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -26,9 +26,7 @@ impl IsHierarchicalForm for BeAnyMut { impl IsDynCompatibleForm for BeAnyMut { type DynLeaf<'a, D: 'static + ?Sized> = AnyMut<'a, D>; -} -impl IsDynMappableForm for BeAnyMut { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 3c694a34..a58c423e 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -27,9 +27,7 @@ impl IsHierarchicalForm for BeAnyRef { impl IsDynCompatibleForm for BeAnyRef { type DynLeaf<'a, D: 'static + ?Sized> = crate::internal_prelude::AnyRef<'a, D>; -} -impl IsDynMappableForm for BeAnyRef { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 8c6a2cc7..279b7620 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -29,9 +29,7 @@ impl IsHierarchicalForm for BeAssignee { impl IsDynCompatibleForm for BeAssignee { type DynLeaf<'a, D: 'static + ?Sized> = QqqAssignee; -} -impl IsDynMappableForm for BeAssignee { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index f22763ce..5045f777 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -29,9 +29,7 @@ impl IsHierarchicalForm for BeMutable { impl IsDynCompatibleForm for BeMutable { type DynLeaf<'a, D: 'static + ?Sized> = QqqMutable; -} -impl IsDynMappableForm for BeMutable { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 46047dde..046df556 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -22,9 +22,7 @@ impl IsHierarchicalForm for BeOwned { impl IsDynCompatibleForm for BeOwned { type DynLeaf<'a, D: 'static + ?Sized> = Box; -} -impl IsDynMappableForm for BeOwned { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 847ae37b..72536920 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -29,9 +29,7 @@ impl IsHierarchicalForm for BeShared { impl IsDynCompatibleForm for BeShared { type DynLeaf<'a, D: 'static + ?Sized> = QqqShared; -} -impl IsDynMappableForm for BeShared { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index f0537f94..c95e4484 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -30,9 +30,7 @@ impl IsHierarchicalForm for BeMut { impl IsDynCompatibleForm for BeMut { type DynLeaf<'a, D: 'static + ?Sized> = &'a mut D; -} -impl IsDynMappableForm for BeMut { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index 9bb5672e..f33224ba 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -30,9 +30,7 @@ impl IsHierarchicalForm for BeRef { impl IsDynCompatibleForm for BeRef { type DynLeaf<'a, D: 'static + ?Sized> = &'a D; -} -impl IsDynMappableForm for BeRef { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, ) -> Option> diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 4b301dd7..156b5d34 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -62,10 +62,7 @@ pub(crate) trait IsLeafType: + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> - + UpcastTo - + UpcastTo - + UpcastTo - + UpcastTo + + UpcastTo { type Leaf: IsValueLeaf; @@ -76,20 +73,18 @@ pub(crate) trait IsDynType: IsType { type DynContent: ?Sized + 'static; } -pub(crate) trait UpcastTo: - IsHierarchicalType +pub(crate) trait UpcastTo: IsHierarchicalType { - fn upcast_to<'a>(content: Content<'a, Self, F>) -> Content<'a, T, F>; + fn upcast_to<'a, F: IsHierarchicalForm>(content: Content<'a, Self, F>) -> Content<'a, T, F>; } -pub(crate) trait DowncastFrom: - IsHierarchicalType +pub(crate) trait DowncastFrom: IsHierarchicalType { - fn downcast_from<'a>( + fn downcast_from<'a, F: IsHierarchicalForm>( content: Content<'a, T, F>, ) -> Result, Content<'a, T, F>>; - fn resolve<'a>( + fn resolve<'a, F: IsHierarchicalForm>( content: Content<'a, T, F>, span_range: SpanRange, resolution_target: &str, @@ -110,12 +105,11 @@ pub(crate) trait DowncastFrom: } } -pub(crate) trait DynResolveFrom: - IsDynType +pub(crate) trait DynResolveFrom: IsDynType { - fn downcast_from<'a>(content: Content<'a, T, F>) -> Option>; + fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>(content: Content<'a, T, F>) -> Option>; - fn resolve<'a>( + fn resolve<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( content: Content<'a, T, F>, span_range: SpanRange, resolution_target: &str, @@ -196,18 +190,18 @@ pub(crate) use impl_type_feature_resolver; macro_rules! impl_ancestor_chain_conversions { ($child:ty $(=> $parent:ident ($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?) => { - impl DowncastFrom<$child, F> for $child + impl DowncastFrom<$child> for $child { - fn downcast_from<'a>( + fn downcast_from<'a, F: IsHierarchicalForm>( content: Content<'a, Self, F>, ) -> Result, Content<'a, Self, F>> { Ok(content) } } - impl UpcastTo<$child, F> for $child + impl UpcastTo<$child> for $child { - fn upcast_to<'a>( + fn upcast_to<'a, F: IsHierarchicalForm>( content: Content<'a, Self, F>, ) -> Content<'a, Self, F> { content @@ -234,18 +228,18 @@ macro_rules! impl_ancestor_chain_conversions { } } - impl DowncastFrom<$parent, F> for $child + impl DowncastFrom<$parent> for $child { - fn downcast_from<'a>( + fn downcast_from<'a, F: IsHierarchicalForm>( content: Content<'a, $parent, F>, ) -> Result, Content<'a, $parent, F>> { <$child as IsChildType>::from_parent(content) } } - impl UpcastTo<$parent, F> for $child + impl UpcastTo<$parent> for $child { - fn upcast_to<'a>( + fn upcast_to<'a, F: IsHierarchicalForm>( content: Content<'a, $child, F>, ) -> Content<'a, $parent, F> { <$child as IsChildType>::into_parent(content) @@ -253,23 +247,23 @@ macro_rules! impl_ancestor_chain_conversions { } $( - impl DowncastFrom<$ancestor, F> for $child { - fn downcast_from<'a>( + impl DowncastFrom<$ancestor> for $child { + fn downcast_from<'a, F: IsHierarchicalForm>( content: Content<'a, $ancestor, F>, ) -> Result, Content<'a, $ancestor, F>> { - let inner = <$parent as DowncastFrom<$ancestor, F>>::downcast_from(content)?; - match <$child as DowncastFrom<$parent, F>>::downcast_from(inner) { + let inner = <$parent as DowncastFrom<$ancestor>>::downcast_from::(content)?; + match <$child as DowncastFrom<$parent>>::downcast_from::(inner) { Ok(c) => Ok(c), - Err(existing) => Err(<$parent as UpcastTo<$ancestor, F>>::upcast_to(existing)), + Err(existing) => Err(<$parent as UpcastTo<$ancestor>>::upcast_to::(existing)), } } } - impl UpcastTo<$ancestor, F> for $child { - fn upcast_to<'a>( + impl UpcastTo<$ancestor> for $child { + fn upcast_to<'a, F: IsHierarchicalForm>( content: Content<'a, $child, F>, ) -> Content<'a, $ancestor, F> { - <$parent as UpcastTo<$ancestor, F>>::upcast_to(<$child as UpcastTo<$parent, F>>::upcast_to(content)) + <$parent as UpcastTo<$ancestor>>::upcast_to::(<$child as UpcastTo<$parent>>::upcast_to::(content)) } } )* @@ -804,14 +798,14 @@ macro_rules! define_dyn_type { type Type = $type_def; } - impl DynResolveFrom for $type_def + impl DynResolveFrom for $type_def { - fn downcast_from<'a>(content: Content<'a, T, F>) -> Option> { + fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>(content: Content<'a, T, F>) -> Option> { T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content) } } - impl LeafMapper for DynMapper<$dyn_type> { + impl LeafMapper for DynMapper<$dyn_type> { type Output<'a, T: IsHierarchicalType> = Option>; fn to_parent_output<'a, T: IsChildType>( From 9fcc3ffe74bff97d46278b4da5f57226b489d326 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 17 Jan 2026 18:01:37 +0000 Subject: [PATCH 53/58] fix: Fix styling --- src/expressions/concepts/content.rs | 3 ++- .../concepts/forms/copy_on_write.rs | 15 +++++++-------- src/expressions/concepts/type_traits.rs | 13 ++++++------- src/misc/mut_rc_ref_cell.rs | 18 ++++++++---------- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 406697ef..737f8caf 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -98,7 +98,8 @@ where { let Spanned(value, span_range) = self; let content = value.into_content(); - let resolved = <::Type>::resolve::(content, span_range, description)?; + let resolved = + <::Type>::resolve::(content, span_range, description)?; Ok(X::from_spanned_content(Spanned(resolved, span_range))) } } diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 5f81d11c..7badc3cf 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -192,14 +192,14 @@ where } // TODO - Find alternative implementation or replace - fn map(self, mapper: M) -> ExecutionResult> - where - 'a: 'static, - M: TypeMapper, // u32 is temporary to see if we can make it work without adding generics to `map_via_leaf!` - Self: Sized, - Self: IsValueContent, // Temporary to see if we can make it work without adding generics to `map_via_leaf!` + fn map(self, mapper: M) -> ExecutionResult> + where + 'a: 'static, + M: OwnedTypeMapper + RefTypeMapper, + M: TypeMapper, // u32 is temporary to see if we can make it work without adding generics to `map_via_leaf!` + Self: Sized, + Self: IsValueContent, // Temporary to see if we can make it work without adding generics to `map_via_leaf!` { - Ok(match self.into_any_level_copy_on_write() { AnyLevelCopyOnWrite::Owned(owned) => { BeCopyOnWrite::new_owned::(mapper.map_owned(owned)?) @@ -237,7 +237,6 @@ where } } - fn inner_map_ref<'a>( ref_value: Content<'a, AnyType, BeRef>, ) -> ExecutionResult<&'a Content<'static, AnyType, BeOwned>> { diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 156b5d34..c18903b4 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -73,13 +73,11 @@ pub(crate) trait IsDynType: IsType { type DynContent: ?Sized + 'static; } -pub(crate) trait UpcastTo: IsHierarchicalType -{ +pub(crate) trait UpcastTo: IsHierarchicalType { fn upcast_to<'a, F: IsHierarchicalForm>(content: Content<'a, Self, F>) -> Content<'a, T, F>; } -pub(crate) trait DowncastFrom: IsHierarchicalType -{ +pub(crate) trait DowncastFrom: IsHierarchicalType { fn downcast_from<'a, F: IsHierarchicalForm>( content: Content<'a, T, F>, ) -> Result, Content<'a, T, F>>; @@ -105,9 +103,10 @@ pub(crate) trait DowncastFrom: IsHierarchicalType } } -pub(crate) trait DynResolveFrom: IsDynType -{ - fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>(content: Content<'a, T, F>) -> Option>; +pub(crate) trait DynResolveFrom: IsDynType { + fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( + content: Content<'a, T, F>, + ) -> Option>; fn resolve<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( content: Content<'a, T, F>, diff --git a/src/misc/mut_rc_ref_cell.rs b/src/misc/mut_rc_ref_cell.rs index d978afd3..8885b46d 100644 --- a/src/misc/mut_rc_ref_cell.rs +++ b/src/misc/mut_rc_ref_cell.rs @@ -162,7 +162,10 @@ impl SharedSubRcRefCell { } } - pub(crate) fn map(self, f: impl for<'a> FnOnce(&'a U) -> &'a V) -> SharedSubRcRefCell { + pub(crate) fn map( + self, + f: impl for<'a> FnOnce(&'a U) -> &'a V, + ) -> SharedSubRcRefCell { SharedSubRcRefCell { shared_ref: Ref::map(self.shared_ref, f), pointed_at: self.pointed_at, @@ -209,7 +212,7 @@ impl SharedSubRcRefCell { Encapsulator { inner: self, encapsulation_lifetime: std::marker::PhantomData, - } + }, ) } @@ -239,22 +242,17 @@ impl SharedSubRcRefCell { } } - pub(crate) struct Encapsulator<'a, T: ?Sized, U: 'static + ?Sized> { inner: SharedSubRcRefCell, encapsulation_lifetime: std::marker::PhantomData<&'a ()>, } -impl <'a, T: 'static + ?Sized, U: 'static + ?Sized> Encapsulator<'a, T, U> { - pub(crate) fn encapsulate( - self, - value: &'a V, - ) -> SharedSubRcRefCell { +impl<'a, T: 'static + ?Sized, U: 'static + ?Sized> Encapsulator<'a, T, U> { + pub(crate) fn encapsulate(self, value: &'a V) -> SharedSubRcRefCell { self.inner.map(|_| // SAFETY: The lifetime 'a is equal to the &'a content argument in replace // So this guarantees that the returned reference is valid as long as the SharedSubRcRefCell exists - unsafe { less_buggy_transmute::<&'a V, &'static V>(value) } - ) + unsafe { less_buggy_transmute::<&'a V, &'static V>(value) }) } } From fa94cd83e0481c8aea4b8fb3f874ca2157851c3e Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 17 Jan 2026 19:03:14 +0000 Subject: [PATCH 54/58] feat: Resolve property/index access from type --- src/expressions/concepts/type_traits.rs | 18 ++ src/expressions/evaluation/evaluator.rs | 15 ++ src/expressions/evaluation/value_frames.rs | 72 ++++++-- .../type_resolution/interface_macros.rs | 170 +++++++++++++++++- src/expressions/type_resolution/type_data.rs | 86 +++++++++ src/expressions/values/any_value.rs | 74 -------- src/expressions/values/array.rs | 11 ++ src/expressions/values/object.rs | 22 +++ 8 files changed, 374 insertions(+), 94 deletions(-) diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index c18903b4..61cb758b 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -181,6 +181,24 @@ macro_rules! impl_type_feature_resolver { // Purposefully doesn't resolve parents, but TBC if this is right <$type_def as TypeData>::resolve_type_property(property_name) } + + fn resolve_property_access(&self) -> Option { + $( + if let Some(interface) = <$type_defs as TypeData>::resolve_own_property_access() { + return Some(interface); + }; + )+ + None + } + + fn resolve_index_access(&self) -> Option { + $( + if let Some(interface) = <$type_defs as TypeData>::resolve_own_index_access() { + return Some(interface); + }; + )+ + None + } } }; } diff --git a/src/expressions/evaluation/evaluator.rs b/src/expressions/evaluation/evaluator.rs index ca3601d6..0348ec2d 100644 --- a/src/expressions/evaluation/evaluator.rs +++ b/src/expressions/evaluation/evaluator.rs @@ -212,6 +212,21 @@ impl RequestedValue { } } + /// Returns the leaf kind of the underlying value, for type resolution purposes. + pub(crate) fn value_kind(&self) -> AnyValueLeafKind { + match self { + RequestedValue::Owned(value) => value.value_kind(), + RequestedValue::Shared(shared) => shared.value_kind(), + RequestedValue::Mutable(mutable) => mutable.value_kind(), + RequestedValue::CopyOnWrite(cow) => cow.value_kind(), + RequestedValue::Assignee(assignee) => assignee.value_kind(), + RequestedValue::LateBound(late_bound) => late_bound.value_kind(), + RequestedValue::AssignmentCompletion(_) => { + panic!("value_kind() called on AssignmentCompletion") + } + } + } + pub(crate) fn expect_any_value_and_map( self, map_shared: impl FnOnce(AnyValueShared) -> ExecutionResult, diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 8a27d767..033e6cba 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -958,15 +958,29 @@ impl EvaluationFrame for ValuePropertyAccessBuilder { context: ValueContext, Spanned(value, source_span): Spanned, ) -> ExecutionResult { + // Get the source kind to check if property access is supported + let source_kind = value.value_kind(); + + // Query type resolution for property access capability + let Some(interface) = source_kind.feature_resolver().resolve_property_access() else { + return self.access.type_err(format!( + "Cannot access properties on {}", + source_kind.articled_display_name() + )); + }; + + let ctx = PropertyAccessCallContext { + property: &self.access, + }; let auto_create = context.requested_ownership().requests_auto_create(); + + // Execute the access via the interface let mapped = value.expect_any_value_and_map( - |shared| shared.try_map(|value| value.as_ref_value().property_ref(&self.access)), - |mutable| { - mutable - .try_map(|value| value.as_mut_value().property_mut(&self.access, auto_create)) - }, - |owned| owned.into_property(&self.access), + |shared| shared.try_map(|value| (interface.shared_access)(ctx, value)), + |mutable| mutable.try_map(|value| (interface.mutable_access)(ctx, value, auto_create)), + |owned| (interface.owned_access)(ctx, owned), )?; + // The result span covers source through property let result_span = SpanRange::new_between(source_span, self.access.span_range()); context.return_not_necessarily_matching_requested(Spanned(mapped, result_span)) @@ -979,8 +993,13 @@ pub(super) struct ValueIndexAccessBuilder { } enum IndexPath { - OnSourceBranch { index: ExpressionNodeId }, - OnIndexBranch { source: Spanned }, + OnSourceBranch { + index: ExpressionNodeId, + }, + OnIndexBranch { + source: Spanned, + interface: IndexAccessInterface, + }, } impl ValueIndexAccessBuilder { @@ -1018,33 +1037,48 @@ impl EvaluationFrame for ValueIndexAccessBuilder { ) -> ExecutionResult { Ok(match self.state { IndexPath::OnSourceBranch { index } => { + // Get the source kind to check if index access is supported + let source_kind = value.value_kind(); + + // Query type resolution for index access capability + let Some(interface) = source_kind.feature_resolver().resolve_index_access() else { + return self.access.type_err(format!( + "Cannot index into {}", + source_kind.articled_display_name() + )); + }; + + // Store the interface for the next phase + let index_ownership = interface.index_ownership; self.state = IndexPath::OnIndexBranch { source: Spanned(value, span), + interface, }; - // This is a value, so we are _accessing it_ and can't create values - // (that's only possible in a place!) - therefore we don't need an owned key, - // and can use &index for reading values from our array - context.request_shared(self, index) + + // Request the index with ownership specified by the interface + context.request_argument_value(self, index, index_ownership) } IndexPath::OnIndexBranch { source: Spanned(source, source_span), + interface, } => { let index = value.expect_shared(); let index = index.as_ref_value().spanned(span); + let ctx = IndexAccessCallContext { + access: &self.access, + }; let auto_create = context.requested_ownership().requests_auto_create(); + + // Execute the access via the interface let result = source.expect_any_value_and_map( - |shared| { - shared.try_map(|value| value.as_ref_value().index_ref(self.access, index)) - }, + |shared| shared.try_map(|value| (interface.shared_access)(ctx, value, index)), |mutable| { mutable.try_map(|value| { - value - .as_mut_value() - .index_mut(self.access, index, auto_create) + (interface.mutable_access)(ctx, value, index, auto_create) }) }, - |owned| owned.into_indexed(self.access, index), + |owned| (interface.owned_access)(ctx, owned, index), )?; let result_span = SpanRange::new_between(source_span, self.access.span_range()); context.return_not_necessarily_matching_requested(Spanned(result, result_span))? diff --git a/src/expressions/type_resolution/interface_macros.rs b/src/expressions/type_resolution/interface_macros.rs index 4caa3528..0786469f 100644 --- a/src/expressions/type_resolution/interface_macros.rs +++ b/src/expressions/type_resolution/interface_macros.rs @@ -308,6 +308,100 @@ where f(context, A::from_argument(lhs)?, B::from_argument(rhs)?).to_returned_value() } +// ============================================================================ +// Property Access Wrapper Functions +// ============================================================================ + +pub(crate) fn apply_property_shared<'a, S: ResolvableShared + ?Sized + 'a>( + f: for<'b> fn(PropertyAccessCallContext, &'b S) -> ExecutionResult<&'b AnyValue>, + ctx: PropertyAccessCallContext, + source: &'a AnyValue, +) -> ExecutionResult<&'a AnyValue> { + let source = S::resolve_from_ref( + source, + ResolutionContext::new(&ctx.property.span_range(), "The property access source"), + )?; + f(ctx, source) +} + +pub(crate) fn apply_property_mutable<'a, S: ResolvableMutable + ?Sized + 'a>( + f: for<'b> fn(PropertyAccessCallContext, &'b mut S, bool) -> ExecutionResult<&'b mut AnyValue>, + ctx: PropertyAccessCallContext, + source: &'a mut AnyValue, + auto_create: bool, +) -> ExecutionResult<&'a mut AnyValue> { + let source = S::resolve_from_mut( + source, + ResolutionContext::new(&ctx.property.span_range(), "The property access source"), + )?; + f(ctx, source, auto_create) +} + +pub(crate) fn apply_property_owned>( + f: fn(PropertyAccessCallContext, S) -> ExecutionResult, + ctx: PropertyAccessCallContext, + source: AnyValue, +) -> ExecutionResult { + let source = S::resolve_from_value( + source, + ResolutionContext::new(&ctx.property.span_range(), "The property access source"), + )?; + f(ctx, source) +} + +// ============================================================================ +// Index Access Wrapper Functions +// ============================================================================ + +pub(crate) fn apply_index_shared<'a, S: ResolvableShared + ?Sized + 'a>( + f: for<'b> fn( + IndexAccessCallContext, + &'b S, + Spanned, + ) -> ExecutionResult<&'b AnyValue>, + ctx: IndexAccessCallContext, + source: &'a AnyValue, + index: Spanned, +) -> ExecutionResult<&'a AnyValue> { + let source = S::resolve_from_ref( + source, + ResolutionContext::new(&ctx.access.span_range(), "The index access source"), + )?; + f(ctx, source, index) +} + +pub(crate) fn apply_index_mutable<'a, S: ResolvableMutable + ?Sized + 'a>( + f: for<'b> fn( + IndexAccessCallContext, + &'b mut S, + Spanned, + bool, + ) -> ExecutionResult<&'b mut AnyValue>, + ctx: IndexAccessCallContext, + source: &'a mut AnyValue, + index: Spanned, + auto_create: bool, +) -> ExecutionResult<&'a mut AnyValue> { + let source = S::resolve_from_mut( + source, + ResolutionContext::new(&ctx.access.span_range(), "The index access source"), + )?; + f(ctx, source, index, auto_create) +} + +pub(crate) fn apply_index_owned>( + f: fn(IndexAccessCallContext, S, Spanned) -> ExecutionResult, + ctx: IndexAccessCallContext, + source: AnyValue, + index: Spanned, +) -> ExecutionResult { + let source = S::resolve_from_value( + source, + ResolutionContext::new(&ctx.access.span_range(), "The index access source"), + )?; + f(ctx, source, index) +} + pub(crate) struct MethodCallContext<'a> { pub interpreter: &'a mut Interpreter, pub output_span_range: SpanRange, @@ -359,6 +453,16 @@ macro_rules! define_type_features { $([$binary_context:ident])? fn $binary_name:ident($($binary_args:tt)*) $(-> $binary_output_ty:ty)? $([ignore_type_assertion $binary_ignore_type_assertion:tt])? $binary_body:block )* } + $(property_access($property_source_ty:ty) { + $([$property_shared_context:ident])? fn shared($($property_shared_args:tt)*) $property_shared_body:block + $([$property_mutable_context:ident])? fn mutable($($property_mutable_args:tt)*) $property_mutable_body:block + $([$property_owned_context:ident])? fn owned($($property_owned_args:tt)*) $property_owned_body:block + })? + $(index_access($index_source_ty:ty) { + $([$index_shared_context:ident])? fn shared($($index_shared_args:tt)*) $index_shared_body:block + $([$index_mutable_context:ident])? fn mutable($($index_mutable_args:tt)*) $index_mutable_body:block + $([$index_owned_context:ident])? fn owned($($index_owned_args:tt)*) $index_owned_body:block + })? interface_items { $($items:item)* } @@ -396,6 +500,8 @@ macro_rules! define_type_features { {$(assert_output_type::<$binary_output_ty>();)?} } )* + // Note: property_access and index_access source types are verified + // at compile time through the apply_* wrapper functions } $mod_methods_vis mod methods { @@ -458,6 +564,49 @@ macro_rules! define_type_features { )* } + $( + pub(crate) mod property_access { + #[allow(unused)] + use super::*; + + pub(crate) fn shared<'a>(if_empty!([$($property_shared_context)?][_ctx]): PropertyAccessCallContext, $($property_shared_args)*) -> ExecutionResult<&'a AnyValue> $property_shared_body + + pub(crate) fn mutable<'a>(if_empty!([$($property_mutable_context)?][_ctx]): PropertyAccessCallContext, $($property_mutable_args)*) -> ExecutionResult<&'a mut AnyValue> $property_mutable_body + + pub(crate) fn owned(if_empty!([$($property_owned_context)?][_ctx]): PropertyAccessCallContext, $($property_owned_args)*) -> ExecutionResult $property_owned_body + } + + pub(crate) fn property_access_interface() -> PropertyAccessInterface { + PropertyAccessInterface { + shared_access: |ctx, source| apply_property_shared::<$property_source_ty>(property_access::shared, ctx, source), + mutable_access: |ctx, source, auto_create| apply_property_mutable::<$property_source_ty>(property_access::mutable, ctx, source, auto_create), + owned_access: |ctx, source| apply_property_owned::<$property_source_ty>(property_access::owned, ctx, source), + } + } + )? + + $( + pub(crate) mod index_access { + #[allow(unused)] + use super::*; + + pub(crate) fn shared<'a>(if_empty!([$($index_shared_context)?][_ctx]): IndexAccessCallContext, $($index_shared_args)*) -> ExecutionResult<&'a AnyValue> $index_shared_body + + pub(crate) fn mutable<'a>(if_empty!([$($index_mutable_context)?][_ctx]): IndexAccessCallContext, $($index_mutable_args)*) -> ExecutionResult<&'a mut AnyValue> $index_mutable_body + + pub(crate) fn owned(if_empty!([$($index_owned_context)?][_ctx]): IndexAccessCallContext, $($index_owned_args)*) -> ExecutionResult $index_owned_body + } + + pub(crate) fn index_access_interface() -> IndexAccessInterface { + IndexAccessInterface { + index_ownership: ArgumentOwnership::Shared, + shared_access: |ctx, source, index| apply_index_shared::<$index_source_ty>(index_access::shared, ctx, source, index), + mutable_access: |ctx, source, index, auto_create| apply_index_mutable::<$index_source_ty>(index_access::mutable, ctx, source, index, auto_create), + owned_access: |ctx, source, index| apply_index_owned::<$index_source_ty>(index_access::owned, ctx, source, index), + } + } + )? + impl TypeData for $type_def { #[allow(unreachable_code)] fn resolve_own_method(method_name: &str) -> Option { @@ -469,12 +618,31 @@ macro_rules! define_type_features { }) } + define_type_features!(@property_access_impl $($property_source_ty)?); + define_type_features!(@index_access_impl $($index_source_ty)?); + // Pass through resolve_own_unary_operation and resolve_own_binary_operation // until there's a better way to define them $($items)* } } - } + }; + + // Helper rules for generating resolve_own_property_access when property_access is defined + (@property_access_impl $source_ty:ty) => { + fn resolve_own_property_access() -> Option { + Some(property_access_interface()) + } + }; + (@property_access_impl) => {}; + + // Helper rules for generating resolve_own_index_access when index_access is defined + (@index_access_impl $source_ty:ty) => { + fn resolve_own_index_access() -> Option { + Some(index_access_interface()) + } + }; + (@index_access_impl) => {}; } #[cfg(test)] diff --git a/src/expressions/type_resolution/type_data.rs b/src/expressions/type_resolution/type_data.rs index 646f858d..c7877739 100644 --- a/src/expressions/type_resolution/type_data.rs +++ b/src/expressions/type_resolution/type_data.rs @@ -19,6 +19,18 @@ pub(crate) trait TypeFeatureResolver { /// Resolves a property of this type. fn resolve_type_property(&self, _property_name: &str) -> Option; + + /// Resolves property access capability for this type (e.g., `obj.field`). + /// Returns Some if this type supports property access, None otherwise. + fn resolve_property_access(&self) -> Option { + None + } + + /// Resolves index access capability for this type (e.g., `arr[0]`). + /// Returns Some if this type supports indexing, None otherwise. + fn resolve_index_access(&self) -> Option { + None + } } pub(crate) trait TypeData { @@ -48,6 +60,16 @@ pub(crate) trait TypeData { fn resolve_type_property(_property_name: &str) -> Option { None } + + /// Returns the property access interface for this type, if supported. + fn resolve_own_property_access() -> Option { + None + } + + /// Returns the index access interface for this type, if supported. + fn resolve_own_index_access() -> Option { + None + } } #[allow(unused)] @@ -292,3 +314,67 @@ impl BinaryOperationInterface { self.rhs_ownership } } + +// ============================================================================ +// Property Access Interface +// ============================================================================ + +/// Context provided to property access methods. +#[derive(Clone, Copy)] +pub(crate) struct PropertyAccessCallContext<'a> { + pub property: &'a PropertyAccess, +} + +/// Interface for property access on a type (e.g., `obj.field`). +/// +/// Unlike unary/binary operations which return owned values, property access +/// returns references into the source value. This requires three separate +/// access methods for shared, mutable, and owned access patterns. +pub(crate) struct PropertyAccessInterface { + /// Access a property by shared reference. + pub shared_access: + for<'a> fn(PropertyAccessCallContext, &'a AnyValue) -> ExecutionResult<&'a AnyValue>, + /// Access a property by mutable reference, optionally auto-creating if missing. + pub mutable_access: for<'a> fn( + PropertyAccessCallContext, + &'a mut AnyValue, + bool, + ) -> ExecutionResult<&'a mut AnyValue>, + /// Extract a property from an owned value. + pub owned_access: fn(PropertyAccessCallContext, AnyValue) -> ExecutionResult, +} + +// ============================================================================ +// Index Access Interface +// ============================================================================ + +/// Context provided to index access methods. +#[derive(Clone, Copy)] +pub(crate) struct IndexAccessCallContext<'a> { + pub access: &'a IndexAccess, +} + +/// Interface for index access on a type (e.g., `arr[0]` or `obj["key"]`). +/// +/// Similar to property access, but the index is an evaluated expression +/// rather than a static identifier. +pub(crate) struct IndexAccessInterface { + /// The ownership requirement for the index value. + pub index_ownership: ArgumentOwnership, + /// Access an element by shared reference. + pub shared_access: for<'a> fn( + IndexAccessCallContext, + &'a AnyValue, + Spanned, + ) -> ExecutionResult<&'a AnyValue>, + /// Access an element by mutable reference, optionally auto-creating if missing. + pub mutable_access: for<'a> fn( + IndexAccessCallContext, + &'a mut AnyValue, + Spanned, + bool, + ) -> ExecutionResult<&'a mut AnyValue>, + /// Extract an element from an owned value. + pub owned_access: + fn(IndexAccessCallContext, AnyValue, Spanned) -> ExecutionResult, +} diff --git a/src/expressions/values/any_value.rs b/src/expressions/values/any_value.rs index 632865c1..3db19515 100644 --- a/src/expressions/values/any_value.rs +++ b/src/expressions/values/any_value.rs @@ -4,7 +4,6 @@ pub(crate) type AnyValue = AnyValueContent<'static, BeOwned>; /// For symmetry pub(crate) type AnyValueOwned = AnyValue; pub(crate) type AnyValueRef<'a> = AnyValueContent<'a, BeRef>; -pub(crate) type AnyValueMut<'a> = AnyValueContent<'a, BeMut>; pub(crate) type AnyValueShared = Shared; pub(crate) type AnyValueMutable = Mutable; pub(crate) type AnyValueAssignee = Assignee; @@ -308,28 +307,6 @@ impl<'a> ValuesEqual for AnyValueRef<'a> { } impl AnyValue { - pub(crate) fn into_indexed( - self, - access: IndexAccess, - index: Spanned, - ) -> ExecutionResult { - match self { - AnyValueContent::Array(array) => array.into_indexed(index), - AnyValueContent::Object(object) => object.into_indexed(index), - other => access.type_err(format!("Cannot index into {}", other.articled_kind())), - } - } - - pub(crate) fn into_property(self, access: &PropertyAccess) -> ExecutionResult { - match self { - AnyValueContent::Object(object) => object.into_property(access), - other => access.type_err(format!( - "Cannot access properties on {}", - other.articled_kind() - )), - } - } - pub(crate) fn into_stream( self, grouping: Grouping, @@ -482,57 +459,6 @@ impl<'a> AnyValueRef<'a> { } Ok(()) } - - pub(crate) fn index_ref( - self, - access: IndexAccess, - index: Spanned>, - ) -> ExecutionResult<&'a AnyValue> { - match self { - AnyValueContent::Array(array) => array.index_ref(index), - AnyValueContent::Object(object) => object.index_ref(index), - other => access.type_err(format!("Cannot index into {}", other.articled_kind())), - } - } - - pub(crate) fn property_ref(self, access: &PropertyAccess) -> ExecutionResult<&'a AnyValue> { - match self { - AnyValueContent::Object(object) => object.property_ref(access), - other => access.type_err(format!( - "Cannot access properties on {}", - other.articled_kind() - )), - } - } -} - -impl<'a> AnyValueMut<'a> { - pub(crate) fn index_mut( - self, - access: IndexAccess, - index: Spanned, - auto_create: bool, - ) -> ExecutionResult<&'a mut AnyValue> { - match self { - AnyValueContent::Array(array) => array.index_mut(index), - AnyValueContent::Object(object) => object.index_mut(index, auto_create), - other => access.type_err(format!("Cannot index into {}", other.articled_kind())), - } - } - - pub(crate) fn property_mut( - self, - access: &PropertyAccess, - auto_create: bool, - ) -> ExecutionResult<&'a mut AnyValue> { - match self { - AnyValueContent::Object(object) => object.property_mut(access, auto_create), - other => access.type_err(format!( - "Cannot access properties on {}", - other.articled_kind() - )), - } - } } pub(crate) struct ToStreamContext<'a> { diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 90deff4c..6e39ddab 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -223,6 +223,17 @@ define_type_features! { lhs.items.extend(rhs.items); } } + index_access(ArrayValue) { + fn shared(source: &'a ArrayValue, index: Spanned) { + source.index_ref(index) + } + fn mutable(source: &'a mut ArrayValue, index: Spanned, _auto_create: bool) { + source.index_mut(index) + } + fn owned(source: ArrayValue, index: Spanned) { + source.into_indexed(index) + } + } interface_items { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index 921ed5f4..cadb8dcf 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -279,6 +279,28 @@ define_type_features! { pub(crate) mod unary_operations { } pub(crate) mod binary_operations {} + property_access(ObjectValue) { + [ctx] fn shared(source: &'a ObjectValue) { + source.property_ref(ctx.property) + } + [ctx] fn mutable(source: &'a mut ObjectValue, auto_create: bool) { + source.property_mut(ctx.property, auto_create) + } + [ctx] fn owned(source: ObjectValue) { + source.into_property(ctx.property) + } + } + index_access(ObjectValue) { + fn shared(source: &'a ObjectValue, index: Spanned) { + source.index_ref(index) + } + fn mutable(source: &'a mut ObjectValue, index: Spanned, auto_create: bool) { + source.index_mut(index, auto_create) + } + fn owned(source: ObjectValue, index: Spanned) { + source.into_indexed(index) + } + } interface_items { } } From aa82ede0d3621566bdc57c072fbe7c60c70012cd Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 18 Jan 2026 00:46:56 +0000 Subject: [PATCH 55/58] refactor: Argument resolution works inside of new system --- plans/TODO.md | 12 +- src/expressions/concepts/content.rs | 53 ++++++--- src/expressions/concepts/form.rs | 2 +- src/expressions/concepts/forms/any_mut.rs | 15 ++- src/expressions/concepts/forms/any_ref.rs | 15 ++- src/expressions/concepts/forms/argument.rs | 3 +- src/expressions/concepts/forms/assignee.rs | 11 +- .../concepts/forms/copy_on_write.rs | 96 ++++++++------- src/expressions/concepts/forms/mutable.rs | 12 +- src/expressions/concepts/forms/owned.rs | 4 +- src/expressions/concepts/forms/shared.rs | 12 +- src/expressions/concepts/forms/simple_mut.rs | 108 ++++++++++++++++- src/expressions/concepts/forms/simple_ref.rs | 77 +++++++++++- src/expressions/concepts/mapping.rs | 18 +-- src/expressions/concepts/type_traits.rs | 75 ++++++++---- src/expressions/control_flow.rs | 2 +- src/expressions/type_resolution/arguments.rs | 62 +++++----- src/expressions/values/any_value.rs | 10 +- src/expressions/values/float_untyped.rs | 8 ++ src/expressions/values/integer.rs | 8 ++ src/expressions/values/integer_untyped.rs | 13 ++ src/expressions/values/iterable.rs | 111 +----------------- src/expressions/values/iterator.rs | 8 +- src/expressions/values/stream.rs | 14 +-- src/expressions/values/string.rs | 32 ++--- src/interpretation/bindings.rs | 41 ++++++- src/interpretation/refs.rs | 101 +++++++++++++++- src/misc/field_inputs.rs | 11 ++ src/misc/iterators.rs | 2 +- src/misc/mut_rc_ref_cell.rs | 106 ++++++++++++++--- 30 files changed, 726 insertions(+), 316 deletions(-) diff --git a/plans/TODO.md b/plans/TODO.md index b118778d..0ef1d7dd 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -253,7 +253,15 @@ First, read the @./2025-11-vision.md - [x] Consider if we even need `MapperOutput` or just some helper functions - [x] Delay resolving the type kind into the error case when downcasting - [x] Create macro to define inline mappers in various forms - - [ ] Attempt to see if I can get rid of needing `leaf_to_content` and maybe `content_to_leaf` by exploiting a trick to bind associated types via a sub-trait (e.g. as I did with `IsValueContent::LeafType> + IsLeafValueContent`) + - [x] Attempt to see if I can get rid of needing `leaf_to_content` and maybe `content_to_leaf` by exploiting a trick to bind associated types via a sub-trait (e.g. as I did with `IsValueContent::LeafType> + IsLeafValueContent`) + - [x] Pivot the argument resolution to work with either old or new values + - [ ] Remove as much from arguments.rs as possible + - [ ] Fix `todo!("Argument")` + - [ ] Pivot the return resolution to work with either old or new values + - [ ] Remove `ResolveAs` + - [ ] Look at better implementations of `FromArgument` + .. potentially via some new selector trait `IsResolvable` with a resolution strategy of `Hierarchichal` | `Dyn` | `Custom` + This will let us implement a more general resolution logic, and amalgamate argument parsing and downcast_resolve/dyn_resolve - [ ] Reproduce `CopyOnWrite` - [ ] Implement `TODO[concepts]: COPY ON WRITE MAPPING` - [ ] `BeCopyOnWrite::owned()` / `shared_as_..` can go via `AnyLevelCopyOnWrite => into_copy_on_write` @@ -665,7 +673,7 @@ type ValueMut<'a> = ValueWhich>; * This could be done by adding GATs (raising MSRV to 1.65) so that TypeData can have a `Ref<'T>`, with `Value::Ref<'T> = ValueRef<'T>`... although we only really need GATs for allowing arbitrary references, not just static `SharedSubRcRefCell` from Shared - * And then `Shared<'t, T>` can wrap a `::Type::Ref<'t, T>` (in the file, this can be encapsulated as a `HasRefType` trait, which can be blanket implemeted for types implementing `..Target`). + * And then `Shared<'t, T>` can wrap a `::Type::Ref<'t, T>` (in the file, this can be emplaced as a `HasRefType` trait, which can be blanket implemeted for types implementing `..Target`). * This would mean e.g. `Shared` could wrap a `&str`. * 6 months later I'm not sure what this means: * And then have a `AdvancedCellRef` store a `::Type::Ref<'T>` which can be owned and we can manually call increase strong count etc on the `RefCell`. diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 737f8caf..810bf330 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -91,17 +91,37 @@ where { pub(crate) fn downcast_resolve>( self, - description: &str, + resolution_target: &str, ) -> ExecutionResult where ::Type: DowncastFrom, { let Spanned(value, span_range) = self; let content = value.into_content(); - let resolved = - <::Type>::resolve::(content, span_range, description)?; + let resolved = <::Type>::resolve::( + content, + span_range, + resolution_target, + )?; Ok(X::from_spanned_content(Spanned(resolved, span_range))) } + + // TODO[concepts]: Change to use a FromSpannedDynContent trait, + // so that it can return a ExecutionResult and avoid needing to specify D. + pub(crate) fn dyn_resolve( + self, + resolution_target: &str, + ) -> ExecutionResult> + where + ::Type: DynResolveFrom, + C::Form: IsDynCompatibleForm, + { + let Spanned(value, span_range) = self; + let content = value.into_content(); + let resolved = + <::Type>::resolve::(content, span_range, resolution_target)?; + Ok(resolved) + } } // TODO[concepts]: Remove eventually, along with IntoValue impl @@ -199,19 +219,20 @@ where // Clashes with other blanket impl it will replace! // -// impl< -// X: FromValueContent<'static, Type = T, Form = F>, -// F: IsForm + MapFromArgument, -// T: TypeData + DowncastFrom, -// > IsArgument for X { -// type ValueType = T; -// const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; -// fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { -// let ownership_mapped = F::from_argument_value(value)?; -// let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; -// Ok(X::from_actual(type_mapped)) -// } -// } +impl< + X: FromValueContent<'static, Type = T, Form = F>, + F: IsForm + MapFromArgument, + T: TypeData + DowncastFrom, + > IsArgument for X +{ + type ValueType = T; + const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; + fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + let ownership_mapped = F::from_argument_value(value)?; + let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; + Ok(X::from_content(type_mapped)) + } +} // Clashes with other blanket impl it will replace! // diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 7fd84f49..f4b8e4fb 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -57,7 +57,7 @@ pub(crate) trait IsDynCompatibleForm: IsHierarchicalForm { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn; } diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index 0ad24af9..5bbaf984 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -29,11 +29,14 @@ impl IsDynCompatibleForm for BeAnyMut { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn, { - leaf.map_optional(::map_mut) + leaf.replace(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => Ok(emplacer.emplace(mapped)), + Err(this) => Err(emplacer.emplace(this)), + }) } } @@ -53,9 +56,11 @@ impl MapFromArgument for BeAnyMut { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; fn from_argument_value( - _value: ArgumentValue, + value: ArgumentValue, ) -> ExecutionResult> { - todo!() - // value.expect_mutable().as_any_mut() + Ok(value + .expect_mutable() + .0 + .replace(|inner, emplacer| inner.as_mut_value().into_mutable_any_mut(emplacer))) } } diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index a58c423e..3e9220cd 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -30,11 +30,14 @@ impl IsDynCompatibleForm for BeAnyRef { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn, { - leaf.map_optional(::map_ref) + leaf.replace(|content, emplacer| match ::map_ref(content) { + Ok(mapped) => Ok(emplacer.emplace(mapped)), + Err(this) => Err(emplacer.emplace(this)), + }) } } @@ -48,9 +51,11 @@ impl MapFromArgument for BeAnyRef { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; fn from_argument_value( - _value: ArgumentValue, + value: ArgumentValue, ) -> ExecutionResult> { - todo!() - // value.expect_shared().as_any_ref() + Ok(value + .expect_shared() + .0 + .replace(|inner, emplacer| inner.as_ref_value().into_shared_any_ref(emplacer))) } } diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index 7fbe82f1..a2ac0e4d 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -39,7 +39,6 @@ impl MapFromArgument for BeArgument { fn from_argument_value( value: ArgumentValue, ) -> ExecutionResult> { - todo!() - // Ok(value) + todo!("Argument") } } diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 279b7620..1a20eef6 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -32,11 +32,15 @@ impl IsDynCompatibleForm for BeAssignee { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn, { - leaf.0.map_optional(::map_mut).map(QqqAssignee) + leaf.0 + .replace(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => Ok(QqqAssignee(emplacer.emplace(mapped))), + Err(this) => Err(QqqAssignee(emplacer.emplace(this))), + }) } } @@ -59,7 +63,6 @@ impl MapFromArgument for BeAssignee { fn from_argument_value( value: ArgumentValue, ) -> ExecutionResult> { - todo!() - // value.expect_assignee() + Ok(value.expect_assignee().into_content()) } } diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 7badc3cf..2e6ea9a0 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -37,33 +37,33 @@ impl IsHierarchicalForm for BeCopyOnWrite { } impl BeCopyOnWrite { - pub(crate) fn new_owned<'a, T: IsHierarchicalType>( - owned: Content<'a, T, BeOwned>, - ) -> Content<'a, T, BeCopyOnWrite> { + pub(crate) fn new_owned<'a, C: IntoValueContent<'a, Form = BeOwned>>( + owned: C, + ) -> Content<'a, C::Type, BeCopyOnWrite> { map_via_leaf! { - input: (Content<'a, T, BeOwned>) = owned, + input: (Content<'a, C::Type, BeOwned>) = owned.into_content(), fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { QqqCopyOnWrite::Owned(leaf) } } } - pub(crate) fn new_shared_in_place_of_owned<'a, T: IsHierarchicalType>( - shared: Content<'a, T, BeShared>, - ) -> Content<'a, T, BeCopyOnWrite> { + pub(crate) fn new_shared_in_place_of_owned<'a, C: IntoValueContent<'a, Form = BeShared>>( + shared: C, + ) -> Content<'a, C::Type, BeCopyOnWrite> { map_via_leaf! { - input: (Content<'a, T, BeShared>) = shared, + input: (Content<'a, C::Type, BeShared>) = shared.into_content(), fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { QqqCopyOnWrite::SharedWithInfallibleCloning(leaf) } } } - pub(crate) fn new_shared_in_place_of_shared<'a, T: IsHierarchicalType>( - shared: Content<'a, T, BeShared>, - ) -> Content<'a, T, BeCopyOnWrite> { + pub(crate) fn new_shared_in_place_of_shared<'a, C: IntoValueContent<'a, Form = BeShared>>( + shared: C, + ) -> Content<'a, C::Type, BeCopyOnWrite> { map_via_leaf! { - input: (Content<'a, T, BeShared>) = shared, + input: (Content<'a, C::Type, BeShared>) = shared.into_content(), fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { QqqCopyOnWrite::SharedWithTransparentCloning(leaf) } @@ -75,10 +75,17 @@ impl MapFromArgument for BeCopyOnWrite { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; fn from_argument_value( - _value: ArgumentValue, + value: ArgumentValue, ) -> ExecutionResult> { - // value.expect_copy_on_write() - todo!() + match value.expect_copy_on_write().inner { + CopyOnWriteInner::Owned(owned) => Ok(BeCopyOnWrite::new_owned(owned)), + CopyOnWriteInner::SharedWithInfallibleCloning(shared) => { + Ok(BeCopyOnWrite::new_shared_in_place_of_owned(shared)) + } + CopyOnWriteInner::SharedWithTransparentCloning(shared) => { + Ok(BeCopyOnWrite::new_shared_in_place_of_shared(shared)) + } + } } } @@ -201,34 +208,35 @@ where Self: IsValueContent, // Temporary to see if we can make it work without adding generics to `map_via_leaf!` { Ok(match self.into_any_level_copy_on_write() { - AnyLevelCopyOnWrite::Owned(owned) => { - BeCopyOnWrite::new_owned::(mapper.map_owned(owned)?) - } + AnyLevelCopyOnWrite::Owned(owned) => BeCopyOnWrite::new_owned(mapper.map_owned(owned)?), AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { - // Apply map, get Shared> - let shared = map_via_leaf! { - input: (Content<'a, Self::Type, BeShared>) = shared, - fn map_leaf(leaf) -> (ExecutionResult>>) { - leaf.try_map(|value_ref| { - let from_ref = value_ref.into_any(); - inner_map_ref(from_ref) // &Content + // // Apply map, get Shared> + // let shared = map_via_leaf! { + // input: (Content<'a, Self::Type, BeShared>) = shared, + // fn map_leaf(leaf) -> (ExecutionResult>>) { + // leaf.try_map(|value_ref| { + // let from_ref = value_ref.into_any(); + // inner_map_ref(from_ref) // &Content - // If inner_map_ref returned a Content<'a, M::TTo, BeRef> - // then we'd need to move the leaf map inside here, but it'd work the same - }) - } - }?; - // Migrate Shared into leaf - let shared_content = shared.replace(|content, encapsulator| { - map_via_leaf! { - input: &'r (Content<'a, AnyType, BeOwned>) = content, - state: | <'r2> Encapsulator<'r2, AnyValue, AnyValue> | let encapsulator = encapsulator, - fn map_leaf(leaf) -> (Content<'static, T, BeShared>) { - encapsulator.encapsulate(leaf) - } - } - }); - BeCopyOnWrite::new_shared_in_place_of_owned::(shared_content) + // // If inner_map_ref returned a Content<'a, M::TTo, BeRef> + // // then we'd need to move the leaf map inside here, but it'd work the same + // }) + // } + // }?; + // // Migrate Shared into leaf + // let shared_content = shared.replace(|content, emplacer| { + // // TODO - use two lifetimes here + // // ---- + // map_via_leaf! { + // input: &'r (Content<'a, AnyType, BeOwned>) = content, + // state: | <'e> SharedEmplacer<'e, AnyValue, AnyValue> | let emplacer = emplacer, + // fn map_leaf(leaf) -> (Content<'static, T, BeShared>) { + // emplacer.emplace(leaf) + // } + // } + // }); + // BeCopyOnWrite::new_shared_in_place_of_owned::(shared_content) + todo!() } AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { todo!() @@ -257,12 +265,12 @@ pub(crate) enum AnyLevelCopyOnWrite<'a, T: IsHierarchicalType> { impl<'a, T: IsHierarchicalType> AnyLevelCopyOnWrite<'a, T> { pub fn into_copy_on_write(self) -> Content<'a, T, BeCopyOnWrite> { match self { - AnyLevelCopyOnWrite::Owned(owned) => BeCopyOnWrite::new_owned::(owned), + AnyLevelCopyOnWrite::Owned(owned) => BeCopyOnWrite::new_owned(owned), AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { - BeCopyOnWrite::new_shared_in_place_of_owned::(shared) + BeCopyOnWrite::new_shared_in_place_of_owned(shared) } AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { - BeCopyOnWrite::new_shared_in_place_of_shared::(shared) + BeCopyOnWrite::new_shared_in_place_of_shared(shared) } } } diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index 5045f777..9d41d11d 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -32,11 +32,14 @@ impl IsDynCompatibleForm for BeMutable { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn, { - leaf.map_optional(::map_mut) + leaf.replace(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => Ok(emplacer.emplace(mapped)), + Err(this) => Err(emplacer.emplace(this)), + }) } } @@ -56,9 +59,8 @@ impl MapFromArgument for BeMutable { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; fn from_argument_value( - _value: ArgumentValue, + value: ArgumentValue, ) -> ExecutionResult> { - // value.expect_mutable() - todo!() + Ok(value.expect_mutable().into_content()) } } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 046df556..f4a27f3e 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -25,11 +25,11 @@ impl IsDynCompatibleForm for BeOwned { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn, { - ::map_boxed(Box::new(leaf)) + ::map_boxed(Box::new(leaf)).map_err(|boxed| *boxed) } } diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 72536920..0eff1b4a 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -32,11 +32,14 @@ impl IsDynCompatibleForm for BeShared { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn, { - leaf.map_optional(::map_ref) + leaf.replace(|content, emplacer| match ::map_ref(content) { + Ok(mapped) => Ok(emplacer.emplace(mapped)), + Err(this) => Err(emplacer.emplace(this)), + }) } } @@ -50,9 +53,8 @@ impl MapFromArgument for BeShared { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; fn from_argument_value( - _value: ArgumentValue, + value: ArgumentValue, ) -> ExecutionResult> { - // value.expect_shared() - todo!() + Ok(value.expect_shared().into_content()) } } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index c95e4484..fcd4bbe4 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -33,7 +33,7 @@ impl IsDynCompatibleForm for BeMut { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn, { @@ -46,3 +46,109 @@ impl LeafAsRefForm for BeMut { leaf } } + +pub(crate) trait IsSelfMutContent<'a>: IsSelfValueContent<'a> +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ + fn into_mutable<'b, T: 'static>( + self, + emplacer: &'b mut MutableEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeMutable> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut MutableEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeMutable>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { self.emplacer.emplace_unchecked(leaf) } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } + + fn into_assignee<'b, T: 'static>( + self, + emplacer: &'b mut MutableEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeAssignee> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut MutableEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAssignee>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { QqqAssignee(self.emplacer.emplace_unchecked(leaf)) } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } + + fn into_mutable_any_mut<'b, T: 'static>( + self, + emplacer: &'b mut MutableEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeAnyMut> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut MutableEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAnyMut>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { Mutable(self.emplacer.emplace_unchecked(leaf)).into() } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } +} + +impl<'a, C: IsSelfValueContent<'a>> IsSelfMutContent<'a> for C +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ +} diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index f33224ba..d466b492 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -33,7 +33,7 @@ impl IsDynCompatibleForm for BeRef { fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( leaf: Self::Leaf<'a, T>, - ) -> Option> + ) -> Result, Content<'a, T, Self>> where T::Leaf: CastDyn, { @@ -46,3 +46,78 @@ impl LeafAsRefForm for BeRef { leaf } } + +pub(crate) trait IsSelfRefContent<'a>: IsSelfValueContent<'a> +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ + fn into_shared<'b, T: 'static>( + self, + emplacer: &'b mut SharedEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeShared> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut SharedEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeShared>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { self.emplacer.emplace_unchecked(leaf) } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } + + fn into_shared_any_ref<'b, T: 'static>( + self, + emplacer: &'b mut SharedEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeAnyRef> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut SharedEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAnyRef>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { Shared(self.emplacer.emplace_unchecked(leaf)).into() } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } +} + +impl<'a, C: IsSelfValueContent<'a>> IsSelfRefContent<'a> for C +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ +} diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs index c473a58c..6b58a307 100644 --- a/src/expressions/concepts/mapping.rs +++ b/src/expressions/concepts/mapping.rs @@ -62,17 +62,17 @@ pub(crate) trait MutLeafMapper { macro_rules! map_via_leaf { ( input: $(&$r:lifetime $($mut:ident)?)? (Content<$a:lifetime, $input_type:ty, $input_form:ty>) = $input_value:expr, - $(state: $(| <$state_l:lifetime>)? $state_type:ty | let $state_pat:pat = $state_init:expr,)? + $(state: $(| <$($state_l:lifetime,)* $($state_t:ident = $state_t_ty:ty,)*>)? $state_type:ty | let $state_pat:pat = $state_init:expr,)? fn map_leaf <$f:ident $(: $fb:ident $(+ $fbe:ident )*)? $(= $fixed_form:ty)?, $t:ident> ($leaf:ident) -> ($($output_type:tt)+) $(where $($where_clause:tt)*)? $body:block ) => {{ - struct __InlineMapper $($(<$state_l>)?)? { + struct __InlineMapper $($(<$($state_l,)* $($state_t,)*>)?)? { state: $crate::if_exists!{ {$($state_type)?} {$($state_type)?} {()} }, } $crate::__map_via_leaf_trait_impl!{ - $(@fixed_form $fixed_form |)? impl<$f $(: $fb $(+ $fbe)*)?> @mutability $(&$r $($mut)?)? for __InlineMapper $($(<$state_l>)?)? $(where $($where_clause)*)? + $(@fixed_form $fixed_form |)? impl<$f $(: $fb $(+ $fbe)*)?> @mutability $(&$r $($mut)?)? for __InlineMapper $($(<$($state_l,)* $($state_t,)*>)?)? $(where $($where_clause)*)? { type Output<$($r,)? $a $(: $r)?, $t: $crate::expressions::concepts::IsHierarchicalType> = $($output_type)+; @@ -107,12 +107,12 @@ macro_rules! map_via_leaf { #[doc(hidden)] macro_rules! __map_via_leaf_trait_impl { - (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime mut for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? <$f $(: $fb $(+ $fbe)*)?> MutLeafMapper<$f> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; - (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? <$f $(: $fb $(+ $fbe)*)?> RefLeafMapper<$f> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; - (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? <$f $(: $fb $(+ $fbe)*)?> LeafMapper<$f> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; - (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime mut for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? MutLeafMapper<$fixed_form> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; - (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? RefLeafMapper<$fixed_form> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; - (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability for $mapper:ident $(<$state_l:lifetime>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$state_l>)? LeafMapper<$fixed_form> for $mapper $(<$state_l>)? $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime mut for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? <$f $(: $fb $(+ $fbe)*)?> MutLeafMapper<$f> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? <$f $(: $fb $(+ $fbe)*)?> RefLeafMapper<$f> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? <$f $(: $fb $(+ $fbe)*)?> LeafMapper<$f> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime mut for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? MutLeafMapper<$fixed_form> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? RefLeafMapper<$fixed_form> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? LeafMapper<$fixed_form> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; } #[doc(hidden)] diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 61cb758b..75412710 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -106,23 +106,23 @@ pub(crate) trait DowncastFrom: IsHierarchicalType { pub(crate) trait DynResolveFrom: IsDynType { fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( content: Content<'a, T, F>, - ) -> Option>; + ) -> Result, Content<'a, T, F>>; fn resolve<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( content: Content<'a, T, F>, span_range: SpanRange, resolution_target: &str, ) -> ExecutionResult> { - let leaf_kind = T::content_to_leaf_kind::(&content); let content = match Self::downcast_from(content) { - Some(c) => c, - None => { + Ok(c) => c, + Err(existing) => { + let leaf_kind = T::content_to_leaf_kind::(&existing); return span_range.value_err(format!( "{} is expected to be {}, but it is {}", resolution_target, Self::ARTICLED_DISPLAY_NAME, leaf_kind.articled_display_name(), - )) + )); } }; Ok(content) @@ -319,22 +319,19 @@ where type LeafType = L::Type; } -pub(crate) trait IsDynLeaf: 'static -where - DynMapper: LeafMapper, -{ +pub(crate) trait IsDynLeaf: 'static { type Type: IsDynType; } pub(crate) trait CastDyn { - fn map_boxed(self: Box) -> Option> { - None + fn map_boxed(self: Box) -> Result, Box> { + Err(self) } - fn map_ref(&self) -> Option<&T> { - None + fn map_ref(&self) -> Result<&T, &Self> { + Err(self) } - fn map_mut(&mut self) -> Option<&mut T> { - None + fn map_mut(&mut self) -> Result<&mut T, &mut Self> { + Err(self) } } @@ -693,14 +690,14 @@ macro_rules! define_leaf_type { $($dyn_trait_impl)* } impl CastDyn for $content_type { - fn map_boxed(self: Box) -> Option> { - Some(self) + fn map_boxed(self: Box) -> Result, Box> { + Ok(self) } - fn map_ref(&self) -> Option<&dyn $dyn_trait> { - Some(self) + fn map_ref(&self) -> Result<&dyn $dyn_trait, &Self> { + Ok(self) } - fn map_mut(&mut self) -> Option<&mut dyn $dyn_trait> { - Some(self) + fn map_mut(&mut self) -> Result<&mut dyn $dyn_trait, &mut Self> { + Ok(self) } } )* @@ -815,20 +812,50 @@ macro_rules! define_dyn_type { type Type = $type_def; } + impl IsArgument for Box<$dyn_type> { + type ValueType = $type_def; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + let form_mapped = BeOwned::from_argument_value(value)?; + <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") + } + } + + impl<'a> IsArgument for AnyRef<'a, $dyn_type> { + type ValueType = $type_def; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; + fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + let form_mapped = BeAnyRef::from_argument_value(value)?; + <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") + } + } + + impl<'a> IsArgument for AnyMut<'a, $dyn_type> { + type ValueType = $type_def; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; + fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + let form_mapped = BeAnyMut::from_argument_value(value)?; + <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") + } + } + impl DynResolveFrom for $type_def { - fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>(content: Content<'a, T, F>) -> Option> { + fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>(content: Content<'a, T, F>) -> Result, Content<'a, T, F>> { T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content) } } impl LeafMapper for DynMapper<$dyn_type> { - type Output<'a, T: IsHierarchicalType> = Option>; + type Output<'a, T: IsHierarchicalType> = Result, Content<'a, T, F>>; fn to_parent_output<'a, T: IsChildType>( output: Self::Output<'a, T>, ) -> Self::Output<'a, T::ParentType> { - output + match output { + Ok(dyn_content) => Ok(dyn_content), + Err(content) => Err(T::into_parent(content)), + } } fn map_leaf<'a, T: IsLeafType>( diff --git a/src/expressions/control_flow.rs b/src/expressions/control_flow.rs index 3e1dc39b..1080af25 100644 --- a/src/expressions/control_flow.rs +++ b/src/expressions/control_flow.rs @@ -373,7 +373,7 @@ impl Evaluate for ForExpression { let iterable: IterableValue = self .iterable .evaluate_owned(interpreter)? - .resolve_as("A for loop iterable")?; + .dyn_resolve::("A for loop iterable")?; let span = self.body.span(); let scope = interpreter.current_scope_id(); diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index 9fa5e198..4e272095 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -67,17 +67,17 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgume } } -impl IsArgument for AnyRef<'static, T> -where - Shared: IsArgument, -{ - type ValueType = as IsArgument>::ValueType; - const OWNERSHIP: ArgumentOwnership = as IsArgument>::OWNERSHIP; - - fn from_argument(argument: Spanned) -> ExecutionResult { - Ok(Shared::::from_argument(argument)?.into()) - } -} +// impl IsArgument for AnyRef<'static, T> +// where +// Shared: IsArgument, +// { +// type ValueType = as IsArgument>::ValueType; +// const OWNERSHIP: ArgumentOwnership = as IsArgument>::OWNERSHIP; + +// fn from_argument(argument: Spanned) -> ExecutionResult { +// Ok(Shared::::from_argument(argument)?.into()) +// } +// } impl + ResolvableArgumentTarget + ?Sized> IsArgument for Assignee @@ -99,26 +99,26 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgum } } -impl IsArgument for AnyMut<'static, T> -where - Mutable: IsArgument, -{ - type ValueType = as IsArgument>::ValueType; - const OWNERSHIP: ArgumentOwnership = as IsArgument>::OWNERSHIP; - - fn from_argument(argument: Spanned) -> ExecutionResult { - Ok(Mutable::::from_argument(argument)?.into()) - } -} - -impl + ResolvableArgumentTarget> IsArgument for T { - type ValueType = T::ValueType; - const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - - fn from_argument(argument: Spanned) -> ExecutionResult { - T::resolve_value(argument.expect_owned(), "This argument") - } -} +// impl IsArgument for AnyMut<'static, T> +// where +// Mutable: IsArgument, +// { +// type ValueType = as IsArgument>::ValueType; +// const OWNERSHIP: ArgumentOwnership = as IsArgument>::OWNERSHIP; + +// fn from_argument(argument: Spanned) -> ExecutionResult { +// Ok(Mutable::::from_argument(argument)?.into()) +// } +// } + +// impl + ResolvableArgumentTarget> IsArgument for T { +// type ValueType = T::ValueType; +// const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + +// fn from_argument(argument: Spanned) -> ExecutionResult { +// T::resolve_value(argument.expect_owned(), "This argument") +// } +// } impl + ResolvableArgumentTarget + ToOwned> IsArgument for CopyOnWrite diff --git a/src/expressions/values/any_value.rs b/src/expressions/values/any_value.rs index 3db19515..3c1a6f96 100644 --- a/src/expressions/values/any_value.rs +++ b/src/expressions/values/any_value.rs @@ -4,6 +4,7 @@ pub(crate) type AnyValue = AnyValueContent<'static, BeOwned>; /// For symmetry pub(crate) type AnyValueOwned = AnyValue; pub(crate) type AnyValueRef<'a> = AnyValueContent<'a, BeRef>; +pub(crate) type AnyValueAnyRef<'a> = AnyValueContent<'a, BeAnyRef>; pub(crate) type AnyValueShared = Shared; pub(crate) type AnyValueMutable = Mutable; pub(crate) type AnyValueAssignee = Assignee; @@ -119,7 +120,7 @@ define_type_features! { // EQUALITY METHODS // =============================== // Compare values with strict type checking - errors on value kind mismatch. - [context] fn typed_eq(this: AnyRef, other: AnyRef) -> ExecutionResult { + [context] fn typed_eq(this: AnyValueAnyRef, other: AnyValueAnyRef) -> ExecutionResult { this.as_ref_value().typed_eq(&other.as_ref_value(), context.span_range()) } @@ -167,11 +168,11 @@ define_type_features! { } } pub(crate) mod binary_operations { - fn eq(lhs: AnyRef, rhs: AnyRef) -> bool { + fn eq(lhs: AnyValueAnyRef, rhs: AnyValueAnyRef) -> bool { AnyValue::values_equal(lhs.as_ref_value(), rhs.as_ref_value()) } - fn ne(lhs: AnyRef, rhs: AnyRef) -> bool { + fn ne(lhs: AnyValueAnyRef, rhs: AnyValueAnyRef) -> bool { !AnyValue::values_equal(lhs.as_ref_value(), rhs.as_ref_value()) } } @@ -531,7 +532,8 @@ impl Spanned { self, resolution_target: &str, ) -> ExecutionResult { - IterableValue::resolve_value(self, resolution_target)?.into_iterator() + self.dyn_resolve::(resolution_target)? + .into_iterator() } } diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index cbd1b088..360feb4f 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -190,6 +190,14 @@ define_type_features! { pub(crate) struct UntypedFloatFallback(pub FallbackFloat); +impl IsArgument for UntypedFloatFallback { + type ValueType = UntypedFloatType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(value: Spanned) -> ExecutionResult { + Self::resolve_value(value.expect_owned(), "This argument") + } +} + impl ResolvableArgumentTarget for UntypedFloatFallback { type ValueType = UntypedFloatType; } diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index 59d5a9c5..532f267b 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -593,6 +593,14 @@ impl_resolvable_argument_for! { pub(crate) struct CoercedToU32(pub(crate) u32); +impl IsArgument for CoercedToU32 { + type ValueType = IntegerType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(value: Spanned) -> ExecutionResult { + Self::resolve_value(value.expect_owned(), "This argument") + } +} + impl ResolvableArgumentTarget for CoercedToU32 { type ValueType = IntegerType; } diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index 58721af8..0a38b75a 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -245,6 +245,19 @@ define_type_features! { pub(crate) struct UntypedIntegerFallback(pub(crate) FallbackInteger); +impl IsValueContent for UntypedIntegerFallback { + type Type = IntegerType; + type Form = BeOwned; +} + +impl IsArgument for UntypedIntegerFallback { + type ValueType = UntypedIntegerType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(value: Spanned) -> ExecutionResult { + Self::resolve_value(value.expect_owned(), "This argument") + } +} + impl ResolvableArgumentTarget for UntypedIntegerFallback { type ValueType = UntypedIntegerType; } diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index 6da89aa1..5a87f161 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -13,41 +13,8 @@ define_dyn_type!( articled_display_name: "an iterable (e.g. array, list, etc.)", ); -// If you add a new variant, also update: -// * ResolvableOwned for IterableValue -// * IsArgument for IterableRef -// * The parent of the value's TypeData to be IterableType -pub(crate) enum IterableValue { - Iterator(IteratorValue), - Array(ArrayValue), - Stream(OutputStream), - Object(ObjectValue), - Range(RangeValue), - String(String), -} - -impl ResolvableArgumentTarget for IterableValue { - type ValueType = IterableType; -} - -impl ResolvableOwned for IterableValue { - fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { - Ok(match value { - AnyValue::Array(x) => Self::Array(x), - AnyValue::Object(x) => Self::Object(x), - AnyValue::Stream(x) => Self::Stream(x), - AnyValue::Range(x) => Self::Range(x), - AnyValue::Iterator(x) => Self::Iterator(x), - AnyValue::String(x) => Self::String(x), - _ => { - return context.err( - "an iterable (iterator, array, object, stream, range or string)", - value, - ); - } - }) - } -} +pub(crate) type IterableValue = Box; +pub(crate) type IterableAnyRef<'a> = AnyRef<'a, dyn IsIterable>; define_type_features! { impl IterableType, @@ -57,12 +24,12 @@ define_type_features! { this.into_iterator() } - fn len(this: Spanned) -> ExecutionResult { - this.len() + fn len(Spanned(this, span_range): Spanned) -> ExecutionResult { + this.len(span_range) } - fn is_empty(this: Spanned) -> ExecutionResult { - Ok(this.len()? == 0) + fn is_empty(Spanned(this, span_range): Spanned) -> ExecutionResult { + Ok(this.len(span_range)? == 0) } [context] fn zip(this: IterableValue) -> ExecutionResult { @@ -114,69 +81,3 @@ define_type_features! { } } } - -impl IterableValue { - pub(crate) fn into_iterator(self) -> ExecutionResult { - match self { - IterableValue::Array(value) => Box::new(value).into_iterator(), - IterableValue::Stream(value) => Box::new(value).into_iterator(), - IterableValue::Iterator(value) => Box::new(value).into_iterator(), - IterableValue::Range(value) => Box::new(value).into_iterator(), - IterableValue::Object(value) => Box::new(value).into_iterator(), - IterableValue::String(value) => Box::new(value).into_iterator(), - } - } -} - -pub(crate) enum IterableRef<'a> { - Iterator(AnyRef<'a, IteratorValue>), - Array(AnyRef<'a, ArrayValue>), - Stream(AnyRef<'a, OutputStream>), - Range(AnyRef<'a, RangeValue>), - Object(AnyRef<'a, ObjectValue>), - String(AnyRef<'a, String>), -} - -impl IsArgument for IterableRef<'static> { - type ValueType = IterableType; - const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; - - fn from_argument(argument: Spanned) -> ExecutionResult { - Ok(match argument.kind() { - AnyValueLeafKind::Iterator(_) => { - IterableRef::Iterator(IsArgument::from_argument(argument)?) - } - AnyValueLeafKind::Array(_) => IterableRef::Array(IsArgument::from_argument(argument)?), - AnyValueLeafKind::Stream(_) => { - IterableRef::Stream(IsArgument::from_argument(argument)?) - } - AnyValueLeafKind::Range(_) => IterableRef::Range(IsArgument::from_argument(argument)?), - AnyValueLeafKind::Object(_) => { - IterableRef::Object(IsArgument::from_argument(argument)?) - } - AnyValueLeafKind::String(_) => { - IterableRef::String(IsArgument::from_argument(argument)?) - } - _ => { - return argument.type_err( - "Expected iterable (iterator, array, object, stream, range or string)", - ); - } - }) - } -} - -impl Spanned> { - pub(crate) fn len(&self) -> ExecutionResult { - let Spanned(value, span) = self; - let span = *span; - match value { - IterableRef::Iterator(iterator) => iterator.len(span), - IterableRef::Array(value) => value.len(span), - IterableRef::Stream(value) => ::len(value, span), - IterableRef::Range(value) => value.len(span), - IterableRef::Object(value) => value.len(span), - IterableRef::String(value) => ::len(value, span), - } - } -} diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index e50fdf23..7b284872 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -302,10 +302,10 @@ define_type_features! { } } - fn skip(mut this: IteratorValue, n: usize) -> IteratorValue { + fn skip(mut this: IteratorValue, n: OptionalSuffix) -> IteratorValue { // We make this greedy instead of lazy because the Skip iterator is not clonable. // We return an iterator for forwards compatibility in case we change it. - for _ in 0..n { + for _ in 0..n.0 { if this.next().is_none() { break; } @@ -313,10 +313,10 @@ define_type_features! { this } - fn take(this: IteratorValue, n: usize) -> IteratorValue { + fn take(this: IteratorValue, n: OptionalSuffix) -> IteratorValue { // We collect to a vec to satisfy the clonability requirement, // but only return an iterator for forwards compatibility in case we change it. - let taken = this.take(n).collect::>(); + let taken = this.take(n.0).collect::>(); IteratorValue::new_for_array(ArrayValue::new(taken)) } } diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index b1d427ac..6dadf9f3 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -150,28 +150,28 @@ define_type_features! { [context] fn to_ident(this: Spanned>) -> ExecutionResult { let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); - string_interface::methods::to_ident(context, string.as_str().into_spanned_ref(this.span_range())) + string_interface::methods::to_ident(context, string.into_spanned_ref(this.span_range())) } [context] fn to_ident_camel(this: Spanned>) -> ExecutionResult { let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); - string_interface::methods::to_ident_camel(context, string.as_str().into_spanned_ref(this.span_range())) + string_interface::methods::to_ident_camel(context, string.into_spanned_ref(this.span_range())) } [context] fn to_ident_snake(this: Spanned>) -> ExecutionResult { let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); - string_interface::methods::to_ident_snake(context, string.as_str().into_spanned_ref(this.span_range())) + string_interface::methods::to_ident_snake(context, string.into_spanned_ref(this.span_range())) } [context] fn to_ident_upper_snake(this: Spanned>) -> ExecutionResult { let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); - string_interface::methods::to_ident_upper_snake(context, string.as_str().into_spanned_ref(this.span_range())) + string_interface::methods::to_ident_upper_snake(context, string.into_spanned_ref(this.span_range())) } // Some literals become Value::UnsupportedLiteral but can still be round-tripped back to a stream [context] fn to_literal(this: Spanned>) -> ExecutionResult { let string = this.concat_content(&ConcatBehaviour::literal(this.span_range())); - let literal = string_interface::methods::to_literal(context, string.as_str().into_spanned_ref(this.span_range()))?; + let literal = string_interface::methods::to_literal(context, string.into_spanned_ref(this.span_range()))?; Ok(AnyValue::for_literal(literal).into_any_value()) } @@ -190,7 +190,7 @@ define_type_features! { error_span_range.assertion_err(message.as_str()) } - fn assert(this: Shared, condition: bool, message: Option>) -> ExecutionResult<()> { + fn assert(this: Shared, condition: bool, message: Option>) -> ExecutionResult<()> { if condition { Ok(()) } else { @@ -203,7 +203,7 @@ define_type_features! { } } - fn assert_eq(this: Shared, lhs: Spanned>, rhs: Spanned>, message: Option>) -> ExecutionResult<()> { + fn assert_eq(this: Shared, lhs: Spanned, rhs: Spanned, message: Option>) -> ExecutionResult<()> { match AnyValueRef::debug_eq(&lhs.as_ref_value(), &rhs.as_ref_value()) { Ok(()) => Ok(()), Err(debug_error) => { diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index c7d6b137..5754af4b 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -77,73 +77,73 @@ define_type_features! { // ================== // CONVERSION METHODS // ================== - [context] fn to_ident(this: Spanned>) -> ExecutionResult { + [context] fn to_ident(this: Spanned>) -> ExecutionResult { string_to_ident(&this, &this, context.span_from_join_else_start()) } - [context] fn to_ident_camel(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_camel(this: Spanned>) -> ExecutionResult { let str = string_conversion::to_upper_camel_case(&this); string_to_ident(&str, &this, context.span_from_join_else_start()) } - [context] fn to_ident_snake(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_snake(this: Spanned>) -> ExecutionResult { let str = string_conversion::to_lower_snake_case(&this); string_to_ident(&str, &this, context.span_from_join_else_start()) } - [context] fn to_ident_upper_snake(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_upper_snake(this: Spanned>) -> ExecutionResult { let str = string_conversion::to_upper_snake_case(&this); string_to_ident(&str, &this, context.span_from_join_else_start()) } - [context] fn to_literal(this: Spanned>) -> ExecutionResult { + [context] fn to_literal(this: Spanned>) -> ExecutionResult { string_to_literal(&this, &this, context.span_from_join_else_start()) } // ====================== // STRING RESHAPE METHODS // ====================== - fn to_uppercase(this: AnyRef) -> String { + fn to_uppercase(this: AnyRef) -> String { string_conversion::to_uppercase(&this) } - fn to_lowercase(this: AnyRef) -> String { + fn to_lowercase(this: AnyRef) -> String { string_conversion::to_lowercase(&this) } - fn to_lower_snake_case(this: AnyRef) -> String { + fn to_lower_snake_case(this: AnyRef) -> String { string_conversion::to_lower_snake_case(&this) } - fn to_upper_snake_case(this: AnyRef) -> String { + fn to_upper_snake_case(this: AnyRef) -> String { string_conversion::to_upper_snake_case(&this) } - fn to_kebab_case(this: AnyRef) -> String { + fn to_kebab_case(this: AnyRef) -> String { string_conversion::to_lower_kebab_case(&this) } - fn to_lower_camel_case(this: AnyRef) -> String { + fn to_lower_camel_case(this: AnyRef) -> String { string_conversion::to_lower_camel_case(&this) } - fn to_upper_camel_case(this: AnyRef) -> String { + fn to_upper_camel_case(this: AnyRef) -> String { string_conversion::to_upper_camel_case(&this) } - fn capitalize(this: AnyRef) -> String { + fn capitalize(this: AnyRef) -> String { string_conversion::capitalize(&this) } - fn decapitalize(this: AnyRef) -> String { + fn decapitalize(this: AnyRef) -> String { string_conversion::decapitalize(&this) } - fn to_title_case(this: AnyRef) -> String { + fn to_title_case(this: AnyRef) -> String { string_conversion::title_case(&this) } - fn insert_spaces(this: AnyRef) -> String { + fn insert_spaces(this: AnyRef) -> String { string_conversion::insert_spaces_between_words(&this) } } diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index 463b0725..a5378fa9 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -264,6 +264,19 @@ impl AssigneeValue { } } +impl IsValueContent for Assignee { + type Type = AnyType; + type Form = BeAssignee; +} + +impl IntoValueContent<'static> for Assignee { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + self.0 + .0 + .replace(|inner, emplacer| inner.as_mut_value().into_assignee(emplacer)) + } +} + impl Deref for Assignee { type Target = T; @@ -349,6 +362,18 @@ impl AnyValueMutable { } } +impl IsValueContent for Mutable { + type Type = AnyType; + type Form = BeMutable; +} + +impl IntoValueContent<'static> for Mutable { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + self.0 + .replace(|inner, emplacer| inner.as_mut_value().into_mutable(emplacer)) + } +} + impl AsMut for Mutable { fn as_mut(&mut self) -> &mut T { &mut self.0 @@ -449,6 +474,18 @@ impl AnyValueShared { } } +impl IsValueContent for Shared { + type Type = AnyType; + type Form = BeShared; +} + +impl IntoValueContent<'static> for Shared { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + self.0 + .replace(|inner, emplacer| inner.as_ref_value().into_shared(emplacer)) + } +} + impl AsRef for Shared { fn as_ref(&self) -> &T { &self.0 @@ -465,10 +502,10 @@ impl Deref for Shared { /// Copy-on-write value that can be either owned or shared pub(crate) struct CopyOnWrite { - inner: CopyOnWriteInner, + pub(crate) inner: CopyOnWriteInner, } -enum CopyOnWriteInner { +pub(crate) enum CopyOnWriteInner { /// An owned value that can be used directly Owned(Owned), /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). diff --git a/src/interpretation/refs.rs b/src/interpretation/refs.rs index 776a2a3a..216638ef 100644 --- a/src/interpretation/refs.rs +++ b/src/interpretation/refs.rs @@ -1,7 +1,9 @@ +use std::mem::transmute; + use super::*; /// A flexible type which can either be a reference to a value of type `T`, -/// or an encapsulated reference from a [`Shared`]. +/// or an emplaced reference from a [`Shared`]. pub(crate) struct AnyRef<'a, T: ?Sized + 'static> { inner: AnyRefInner<'a, T>, } @@ -19,6 +21,7 @@ impl<'a, T: ?Sized + 'static> AnyRef<'a, T> { } } + #[allow(unused)] pub(crate) fn map_optional( self, f: impl for<'r> FnOnce(&'r T) -> Option<&'r S>, @@ -32,6 +35,52 @@ impl<'a, T: ?Sized + 'static> AnyRef<'a, T> { }, }) } + + pub(crate) fn replace( + self, + f: impl for<'e> FnOnce(&'e T, &mut AnyRefEmplacer<'a, 'e, T>) -> O, + ) -> O { + let copied_ref = self.deref() as *const T; + let mut emplacer = AnyRefEmplacer { + inner: Some(self), + encapsulation_lifetime: std::marker::PhantomData, + }; + f( + // SAFETY: The underlying reference is valid for the lifetime of self + // So we can copy it fine + unsafe { &*copied_ref }, + &mut emplacer, + ) + } +} + +pub(crate) struct AnyRefEmplacer<'a, 'e: 'a, T: 'static + ?Sized> { + inner: Option>, + encapsulation_lifetime: std::marker::PhantomData<&'e ()>, +} + +impl<'a, 'e: 'a, T: 'static + ?Sized> AnyRefEmplacer<'a, 'e, T> { + pub(crate) fn emplace(&mut self, value: &'e V) -> AnyRef<'a, V> { + unsafe { + // SAFETY: The lifetime 'e is equal to the &'e content argument in replace + // So this guarantees that the returned reference is valid as long as the AnyRef exists + self.emplace_unchecked(value) + } + } + + // SAFETY: + // * The caller must ensure that the value's lifetime is derived from the original content + pub(crate) unsafe fn emplace_unchecked( + &mut self, + value: &V, + ) -> AnyRef<'a, V> { + self.inner + .take() + .expect("You can only emplace to create a new AnyRef value once") + .map(|_| + // SAFETY: As defined in the rustdoc above + unsafe { transmute::<&V, &'static V>(value) }) + } } impl<'a, T: ?Sized> From<&'a T> for AnyRef<'a, T> { @@ -93,7 +142,7 @@ impl<'a, T: 'static + ?Sized> Deref for AnyRef<'a, T> { } /// A flexible type which can either be a mutable reference to a value of type `T`, -/// or an encapsulated reference from a [`Mutable`]. +/// or an emplaced reference from a [`Mutable`]. pub(crate) struct AnyMut<'a, T: 'static + ?Sized> { inner: AnyMutInner<'a, T>, } @@ -122,6 +171,7 @@ impl<'a, T: ?Sized + 'static> AnyMut<'a, T> { } } + #[allow(unused)] pub(crate) fn map_optional( self, f: impl for<'r> FnOnce(&'r mut T) -> Option<&'r mut S>, @@ -135,6 +185,53 @@ impl<'a, T: ?Sized + 'static> AnyMut<'a, T> { }, }) } + + pub(crate) fn replace( + mut self, + f: impl for<'e> FnOnce(&'e mut T, &mut AnyMutEmplacer<'a, 'e, T>) -> O, + ) -> O { + let copied_mut = self.deref_mut() as *mut T; + let mut emplacer = AnyMutEmplacer { + inner: Some(self), + encapsulation_lifetime: std::marker::PhantomData, + }; + f( + // SAFETY: We are cloning a mutable reference here, but it is safe because: + // - What it's pointing at still lives, inside emplacer.inner + // - No other "mutable reference" is created except at encapsulation time + unsafe { &mut *copied_mut }, + &mut emplacer, + ) + } +} + +pub(crate) struct AnyMutEmplacer<'a, 'e: 'a, T: 'static + ?Sized> { + inner: Option>, + encapsulation_lifetime: std::marker::PhantomData<&'e ()>, +} + +impl<'a, 'e: 'a, T: 'static + ?Sized> AnyMutEmplacer<'a, 'e, T> { + pub(crate) fn emplace(&mut self, value: &'e mut V) -> AnyMut<'a, V> { + unsafe { + // SAFETY: The lifetime 'e is equal to the &'e content argument in replace + // So this guarantees that the returned reference is valid as long as the AnyMut exists + self.emplace_unchecked(value) + } + } + + // SAFETY: + // * The caller must ensure that the value's lifetime is derived from the original content + pub(crate) unsafe fn emplace_unchecked( + &mut self, + value: &mut V, + ) -> AnyMut<'a, V> { + self.inner + .take() + .expect("You can only emplace to create a new AnyMut value once") + .map(|_| + // SAFETY: As defined in the rustdoc above + unsafe { transmute::<&mut V, &'static mut V>(value) }) + } } #[allow(unused)] diff --git a/src/misc/field_inputs.rs b/src/misc/field_inputs.rs index 1b63d11e..9d19385a 100644 --- a/src/misc/field_inputs.rs +++ b/src/misc/field_inputs.rs @@ -63,6 +63,17 @@ macro_rules! define_typed_object { )* } + impl IsArgument for $model { + type ValueType = ObjectType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(value: Spanned) -> ExecutionResult { + Self::resolve_value( + value.expect_owned(), + "This argument", + ) + } + } + impl ResolvableArgumentTarget for $model { type ValueType = ObjectType; } diff --git a/src/misc/iterators.rs b/src/misc/iterators.rs index 591f1337..2925a367 100644 --- a/src/misc/iterators.rs +++ b/src/misc/iterators.rs @@ -198,7 +198,7 @@ define_optional_object! { } pub(crate) fn run_intersperse( - items: IterableValue, + items: Box, separator: AnyValue, settings: IntersperseSettings, ) -> ExecutionResult { diff --git a/src/misc/mut_rc_ref_cell.rs b/src/misc/mut_rc_ref_cell.rs index 8885b46d..777e55d0 100644 --- a/src/misc/mut_rc_ref_cell.rs +++ b/src/misc/mut_rc_ref_cell.rs @@ -112,6 +112,59 @@ impl MutableSubRcRefCell { Err(_) => Err(error.unwrap()), } } + + pub(crate) fn replace( + mut self, + f: impl for<'a> FnOnce(&'a mut U, &mut MutableSubEmplacer<'a, T, U>) -> O, + ) -> O { + let ref_mut = self.ref_mut.deref_mut() as *mut U; + let mut emplacer = MutableSubEmplacer { + inner: Some(self), + encapsulation_lifetime: std::marker::PhantomData, + }; + f( + // SAFETY: We are cloning a mutable reference here, but it is safe because: + // - What it's pointing at still lives, as RefMut still lives inside emplacer.inner + // - No other "mutable reference" is created from the RefMut except at encapsulation time + unsafe { &mut *ref_mut }, + &mut emplacer, + ) + } +} + +#[allow(unused)] +pub(crate) type MutableEmplacer<'e, U> = MutableSubEmplacer<'e, AnyValue, U>; + +pub(crate) struct MutableSubEmplacer<'e, T: 'static + ?Sized, U: 'static + ?Sized> { + inner: Option>, + encapsulation_lifetime: std::marker::PhantomData<&'e ()>, +} + +impl<'e, T: 'static + ?Sized, U: 'static + ?Sized> MutableSubEmplacer<'e, T, U> { + pub(crate) fn emplace( + &mut self, + value: &'e mut V, + ) -> MutableSubRcRefCell { + unsafe { + // SAFETY: The lifetime 'e is equal to the &'e content argument in replace + // So this guarantees that the returned reference is valid as long as the MutableSubRcRefCell exists + self.emplace_unchecked(value) + } + } + + // SAFETY: + // * The caller must ensure that the value's lifetime is derived from the original content + pub(crate) unsafe fn emplace_unchecked( + &mut self, + value: &mut V, + ) -> MutableSubRcRefCell { + self.inner + .take() + .expect("You can only emplace to create a new Mutable value once") + .map(|_| + // SAFETY: As defined in the rustdoc above + unsafe { less_buggy_transmute::<&mut V, &'static mut V>(value) }) + } } impl DerefMut for MutableSubRcRefCell { @@ -205,15 +258,14 @@ impl SharedSubRcRefCell { pub(crate) fn replace( self, - f: impl for<'a> FnOnce(&'a U, Encapsulator<'a, T, U>) -> O, + f: impl for<'e> FnOnce(&'e U, &mut SharedSubEmplacer<'e, T, U>) -> O, ) -> O { - f( - &*Ref::clone(&self.shared_ref), - Encapsulator { - inner: self, - encapsulation_lifetime: std::marker::PhantomData, - }, - ) + let copied_ref = Ref::clone(&self.shared_ref); + let mut emplacer = SharedSubEmplacer { + inner: Some(self), + encapsulation_lifetime: std::marker::PhantomData, + }; + f(&*copied_ref, &mut emplacer) } /// SAFETY: @@ -242,17 +294,37 @@ impl SharedSubRcRefCell { } } -pub(crate) struct Encapsulator<'a, T: ?Sized, U: 'static + ?Sized> { - inner: SharedSubRcRefCell, - encapsulation_lifetime: std::marker::PhantomData<&'a ()>, +pub(crate) type SharedEmplacer<'e, U> = SharedSubEmplacer<'e, AnyValue, U>; + +pub(crate) struct SharedSubEmplacer<'e, T: ?Sized, U: 'static + ?Sized> { + inner: Option>, + encapsulation_lifetime: std::marker::PhantomData<&'e ()>, } -impl<'a, T: 'static + ?Sized, U: 'static + ?Sized> Encapsulator<'a, T, U> { - pub(crate) fn encapsulate(self, value: &'a V) -> SharedSubRcRefCell { - self.inner.map(|_| - // SAFETY: The lifetime 'a is equal to the &'a content argument in replace - // So this guarantees that the returned reference is valid as long as the SharedSubRcRefCell exists - unsafe { less_buggy_transmute::<&'a V, &'static V>(value) }) +impl<'e, T: 'static + ?Sized, U: 'static + ?Sized> SharedSubEmplacer<'e, T, U> { + pub(crate) fn emplace( + &mut self, + value: &'e V, + ) -> SharedSubRcRefCell { + unsafe { + // SAFETY: The lifetime 'e is equal to the &'e content argument in replace + // So this guarantees that the returned reference is valid as long as the SharedSubRcRefCell exists + self.emplace_unchecked(value) + } + } + + // SAFETY: + // * The caller must ensure that the value's lifetime is derived from the original content + pub(crate) unsafe fn emplace_unchecked( + &mut self, + value: &V, + ) -> SharedSubRcRefCell { + self.inner + .take() + .expect("You can only emplace to create a new shared value once") + .map(|_| + // SAFETY: As defined in the rustdoc above + unsafe { less_buggy_transmute::<&V, &'static V>(value) }) } } From 5210750e740202f97007e1165a2fd8e71b733a7f Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 18 Jan 2026 01:23:27 +0000 Subject: [PATCH 56/58] refactor: Return values can use new system --- src/expressions/concepts/content.rs | 24 ++++++------ src/expressions/concepts/forms/mutable.rs | 8 ++++ src/expressions/concepts/forms/owned.rs | 8 ++++ src/expressions/concepts/forms/shared.rs | 8 ++++ src/expressions/type_resolution/outputs.rs | 6 --- src/expressions/values/array.rs | 11 ++++-- src/expressions/values/iterator.rs | 22 ++++++++--- src/expressions/values/object.rs | 11 ++++-- src/expressions/values/parser.rs | 44 ++++++++++++++++------ src/expressions/values/range.rs | 13 +++++-- src/expressions/values/stream.rs | 19 ++++++---- src/expressions/values/string.rs | 22 ++++------- src/misc/mod.rs | 9 ++++- 13 files changed, 135 insertions(+), 70 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 810bf330..14a6188e 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -234,16 +234,14 @@ impl< } } -// Clashes with other blanket impl it will replace! -// -// impl< -// X: IntoValueContent<'static, Type = T, Form = F>, -// F: IsForm + MapIntoReturned, -// T: UpcastTo, -// > IsReturnable for X { -// fn to_returned_value(self) -> ExecutionResult { -// let type_mapped = self.into_actual() -// .upcast::(); -// F::into_returned_value(type_mapped) -// } -// } +impl< + X: IntoValueContent<'static, Type = T, Form = F>, + F: IsForm + MapIntoReturned, + T: UpcastTo, + > IsReturnable for X +{ + fn to_returned_value(self) -> ExecutionResult { + let type_mapped = self.into_content().upcast::(); + F::into_returned_value(type_mapped) + } +} diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index 9d41d11d..b17e1c43 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -64,3 +64,11 @@ impl MapFromArgument for BeMutable { Ok(value.expect_mutable().into_content()) } } + +// impl MapIntoReturned for BeMutable { +// fn into_returned_value( +// content: Content<'static, AnyType, Self>, +// ) -> ExecutionResult { +// todo!("Return mutable") +// } +// } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index f4a27f3e..5095ac42 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -55,6 +55,14 @@ impl MapFromArgument for BeOwned { } } +impl MapIntoReturned for BeOwned { + fn into_returned_value( + content: Content<'static, AnyType, Self>, + ) -> ExecutionResult { + Ok(ReturnedValue::Owned(content)) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 0eff1b4a..106cf736 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -58,3 +58,11 @@ impl MapFromArgument for BeShared { Ok(value.expect_shared().into_content()) } } + +// impl MapIntoReturned for BeShared { +// fn into_returned_value( +// content: Content<'static, AnyType, Self>, +// ) -> ExecutionResult { +// todo!("Return shared") +// } +// } diff --git a/src/expressions/type_resolution/outputs.rs b/src/expressions/type_resolution/outputs.rs index f0f9b493..cd823619 100644 --- a/src/expressions/type_resolution/outputs.rs +++ b/src/expressions/type_resolution/outputs.rs @@ -38,12 +38,6 @@ impl IsReturnable for AnyValueMutable { } } -impl IsReturnable for T { - fn to_returned_value(self) -> ExecutionResult { - Ok(ReturnedValue::Owned(self.into_any_value())) - } -} - impl IsReturnable for ExecutionResult { fn to_returned_value(self) -> ExecutionResult { self?.to_returned_value() diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 6e39ddab..25527850 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -170,9 +170,14 @@ impl ValuesEqual for ArrayValue { } } -impl IntoAnyValue for Vec { - fn into_any_value(self) -> AnyValue { - ArrayValue { items: self }.into_any_value() +impl IsValueContent for Vec { + type Type = ArrayType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Vec { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + ArrayValue { items: self } } } diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index 7b284872..b9031076 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -184,15 +184,25 @@ impl IteratorValue { } } -impl IntoAnyValue for IteratorValueInner { - fn into_any_value(self) -> AnyValue { - AnyValue::Iterator(IteratorValue::new(self)) +impl IsValueContent for IteratorValueInner { + type Type = IteratorType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for IteratorValueInner { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + IteratorValue::new(self) } } -impl IntoAnyValue for Box> { - fn into_any_value(self) -> AnyValue { - AnyValue::Iterator(IteratorValue::new_custom(self)) +impl IsValueContent for Box> { + type Type = IteratorType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Box> { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + IteratorValue::new_custom(self) } } diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index cadb8dcf..b410cce6 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -258,9 +258,14 @@ impl Spanned<&ObjectValue> { } } -impl IntoAnyValue for BTreeMap { - fn into_any_value(self) -> AnyValue { - AnyValue::Object(ObjectValue { entries: self }) +impl IsValueContent for BTreeMap { + type Type = ObjectType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for BTreeMap { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + ObjectValue { entries: self } } } diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 89b1e996..492c6ad5 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -270,27 +270,47 @@ impl_resolvable_argument_for! { } } -impl IntoAnyValue for TokenTree { - fn into_any_value(self) -> AnyValue { - OutputStream::new_with(|s| s.push_raw_token_tree(self)).into_any_value() +impl IsValueContent for TokenTree { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for TokenTree { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::new_with(|s| s.push_raw_token_tree(self)) } } -impl IntoAnyValue for Ident { - fn into_any_value(self) -> AnyValue { - OutputStream::new_with(|s| s.push_ident(self)).into_any_value() +impl IsValueContent for Ident { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Ident { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::new_with(|s| s.push_ident(self)) } } -impl IntoAnyValue for Punct { - fn into_any_value(self) -> AnyValue { - OutputStream::new_with(|s| s.push_punct(self)).into_any_value() +impl IsValueContent for Punct { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Punct { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::new_with(|s| s.push_punct(self)) } } -impl IntoAnyValue for Literal { - fn into_any_value(self) -> AnyValue { - OutputStream::new_with(|s| s.push_literal(self)).into_any_value() +impl IsValueContent for Literal { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Literal { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::new_with(|s| s.push_literal(self)) } } diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index 428c9edb..d20da791 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -372,11 +372,16 @@ impl RangeValueInner { } } -impl IntoAnyValue for RangeValueInner { - fn into_any_value(self) -> AnyValue { - AnyValue::Range(RangeValue { +impl IsValueContent for RangeValueInner { + type Type = RangeType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for RangeValueInner { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + RangeValue { inner: Box::new(self), - }) + } } } diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index 6dadf9f3..fc856de3 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -97,9 +97,14 @@ impl ValuesEqual for OutputStream { } } -impl IntoAnyValue for TokenStream { - fn into_any_value(self) -> AnyValue { - OutputStream::raw(self).into_any_value() +impl IsValueContent for TokenStream { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for TokenStream { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::raw(self) } } @@ -179,18 +184,18 @@ define_type_features! { // ============ // NOTE: with_span() exists on all values, this is just a specialized mutable version for streams - fn set_span(mut this: Mutable, span_source: Shared) -> ExecutionResult<()> { + fn set_span(mut this: Mutable, span_source: AnyRef) -> ExecutionResult<()> { let span_range = span_source.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); this.replace_first_level_spans(span_range.join_into_span_else_start()); Ok(()) } - fn error(this: Shared, message: Shared) -> ExecutionResult { + fn error(this: AnyRef, message: AnyRef) -> ExecutionResult { let error_span_range = this.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); error_span_range.assertion_err(message.as_str()) } - fn assert(this: Shared, condition: bool, message: Option>) -> ExecutionResult<()> { + fn assert(this: AnyRef, condition: bool, message: Option>) -> ExecutionResult<()> { if condition { Ok(()) } else { @@ -203,7 +208,7 @@ define_type_features! { } } - fn assert_eq(this: Shared, lhs: Spanned, rhs: Spanned, message: Option>) -> ExecutionResult<()> { + fn assert_eq(this: AnyRef, lhs: Spanned, rhs: Spanned, message: Option>) -> ExecutionResult<()> { match AnyValueRef::debug_eq(&lhs.as_ref_value(), &rhs.as_ref_value()) { Ok(()) => Ok(()), Err(debug_error) => { diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index 5754af4b..99124508 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -42,12 +42,6 @@ impl ValuesEqual for String { } } -impl IntoAnyValue for &str { - fn into_any_value(self) -> AnyValue { - self.to_string().into_any_value() - } -} - pub(crate) fn string_to_ident( str: &str, error_source: &impl HasSpanRange, @@ -153,36 +147,36 @@ define_type_features! { } } pub(crate) mod binary_operations { - fn add(mut lhs: String, rhs: Shared) -> String { + fn add(mut lhs: String, rhs: AnyRef) -> String { lhs.push_str(rhs.deref()); lhs } - fn add_assign(mut lhs: Assignee, rhs: Shared) { + fn add_assign(mut lhs: Assignee, rhs: AnyRef) { lhs.push_str(rhs.deref()); } - fn eq(lhs: Shared, rhs: Shared) -> bool { + fn eq(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() == rhs.deref() } - fn ne(lhs: Shared, rhs: Shared) -> bool { + fn ne(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() != rhs.deref() } - fn lt(lhs: Shared, rhs: Shared) -> bool { + fn lt(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() < rhs.deref() } - fn le(lhs: Shared, rhs: Shared) -> bool { + fn le(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() <= rhs.deref() } - fn ge(lhs: Shared, rhs: Shared) -> bool { + fn ge(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() >= rhs.deref() } - fn gt(lhs: Shared, rhs: Shared) -> bool { + fn gt(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() > rhs.deref() } } diff --git a/src/misc/mod.rs b/src/misc/mod.rs index dd3a9973..36aa4457 100644 --- a/src/misc/mod.rs +++ b/src/misc/mod.rs @@ -38,8 +38,13 @@ pub(crate) fn print_if_slow( // Equivalent to `!` but stable in our MSRV pub(crate) enum Never {} -impl IntoAnyValue for Never { - fn into_any_value(self) -> AnyValue { +impl IsValueContent for Never { + type Type = NoneType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Never { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { match self {} } } From ca93b3660d48774162c52b23594d3a3938f4e9d2 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 18 Jan 2026 21:30:44 +0000 Subject: [PATCH 57/58] fix: Fix MSRV --- src/expressions/concepts/content.rs | 14 +++++++------- src/expressions/concepts/type_traits.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 14a6188e..d33e97c7 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -217,12 +217,10 @@ where { } -// Clashes with other blanket impl it will replace! -// impl< X: FromValueContent<'static, Type = T, Form = F>, - F: IsForm + MapFromArgument, - T: TypeData + DowncastFrom, + F: MapFromArgument, + T: DowncastFrom, > IsArgument for X { type ValueType = T; @@ -235,13 +233,15 @@ impl< } impl< - X: IntoValueContent<'static, Type = T, Form = F>, - F: IsForm + MapIntoReturned, + X: IntoValueContent<'static, Type = T, Form = BeOwned>, + // TODO[concepts]: Migrate to BeOwned => F when it doesn't break MSRV + // due to clashes with `IntoValueContent` on Shared / Mutable + // F: MapIntoReturned, T: UpcastTo, > IsReturnable for X { fn to_returned_value(self) -> ExecutionResult { let type_mapped = self.into_content().upcast::(); - F::into_returned_value(type_mapped) + BeOwned::into_returned_value(type_mapped) } } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index 75412710..e1bd9f26 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -8,7 +8,7 @@ impl TypeVariant for HierarchicalTypeVariant {} pub(crate) struct DynTypeVariant; impl TypeVariant for DynTypeVariant {} -pub(crate) trait IsType: Sized { +pub(crate) trait IsType: Sized + TypeData { type Variant: TypeVariant; const SOURCE_TYPE_NAME: &'static str; From e33ee65058940460eeca0a5fca7b2d5663e47439 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sun, 18 Jan 2026 22:46:13 +0000 Subject: [PATCH 58/58] docs: Update docs --- plans/2026-01-types-and-forms.md | 202 +++++++++++++++++ plans/TODO.md | 251 ++------------------- src/expressions/evaluation/value_frames.rs | 1 - src/expressions/values/array.rs | 4 +- 4 files changed, 229 insertions(+), 229 deletions(-) create mode 100644 plans/2026-01-types-and-forms.md diff --git a/plans/2026-01-types-and-forms.md b/plans/2026-01-types-and-forms.md new file mode 100644 index 00000000..f98009de --- /dev/null +++ b/plans/2026-01-types-and-forms.md @@ -0,0 +1,202 @@ +# Reflection on types and forms - January 2026 + +In December 2025 I embarked on a longer than expected journey, to "migrate forms to leaves": + +```rust +Shared(AnyValue::X(X::Y(y))) => AnyValue::X(X::Y(Shared(y))) +``` + +This was motivated by a few thoughts: +* It would be nice for e.g. a "Shared int" to be convertible to/from a "Shared any value" +* It's common knowledge that an `Option(&x)` is more useful than an `&Option(x)` +* The code around defining types and forms was ugly and repetitive and could do with a tidy up +* I wanted to start on methods/functions, for e.g. a `.map(|x| x * x)` but something in my + early investigation of function variable closures, I realized that this leaf migration would + be needed... both in an abstract sense, but also when I started investigating a "referenceable" + abstraction to be shared between variables and closures. + +### Original justification in the context of methods + +- [ ] Improved Shared/Mutable handling - See the `Better handling of value sub-references` section. The key requirement is we need to allow a `Shared` to map back to a `Shared`. Moving the "sharedness" to the leaves permits this. + * A function specifies the bindings of its variables + * If we have `my_len = |x: &array| x.len()` and invoke it as `my_len(a.b)` then + when I invoke it, I need to end up with the variable `x := &a.b` + * This is a problem - if we imagine changing what can be stored in a variable to + the following, then it's clear that we need some way to have a `SharedValue` which + has an outer-enum instead of an inner-enum. + * We also need to think about how things like `IterableValue` works. Perhaps it's like an interface, and so defined via `Box` / `Ref` etc? + +I don't fully understand what the problem I saw was now. I think that if `my_len` takes a `x: Shared` then to call `x.len()` I need to convert it back to a `Shared` in order to resolve `x.len()`? Maybe? I'm not too sure. + +## The problem + +The problem is that this works fine for covariant things like shared references... but not at all for things like assignee / mutable. In particular, consider: + +```rust + fn swap(mut a: AnyValueAssignee, mut b: AnyValueAssignee) -> () { + core::mem::swap(a.0.deref_mut(), b.0.deref_mut()); + } +``` + +This requires the thing that can be replaced to be the whole `AnyValue`, not just some leaf content. + +In a way, the "everything in the leaf" paradigm corresponds to walking around with `let x: &leaf_type`, restricting the place to only support that particular leaf type. + +## The reflection - the type of a place & type annotations + +So far places have only ever had one type - `any`. Whilst values have leaf types, the places themselves can be swapped to take `AnyValue`. This is pretty much how most dynamically typed languages work. + +If we wanted to support type annotations, they _could_ work like this, by constraining the type the place supports at a structural level... + +Or we could save the trouble and always store an `AnyValue` structurally but insert a runtime type assertion. This is a tiny bit less performant at runtime, but keeps the code simpler AND less code = less compile time, which is more important than faster runtime on smaller codebases that only use preinterpet a bit. + +### What would a hypothetical structurally type-restricted binding look like? + +Sidenote: I don't think we should necessarily support this, for reasons mentioned above. But it would allow us to support something like slices, e.g. `Mutable<[ArrayValue]>` + +Consider a structural `x: &mut integer`. Then, in the type system a value would look something like this: + +```rust +AnyValueContent::Integer(Mutable(IntegerValueContent::U8(u8))) +``` + +Essentially what we have is a form where the form wrapper isn't restricted to being at the leaf; rather it can be at any level of the type! + +But - how would this be modelled? i.e. how would we model a value which could be structurally type-restricted to be any type + +Well it would probably want to look something like: Each parent type having a `Content` enum and `Wrapper` enum, which looks the same, except it has an `AsSelf` variant option as well, i.e.: + +```rust +// &mut integer +AnyValueWrapper::Integer(IntegerValueWrapper::AsSelf(Mutable(IntegerValueContent::U8(u8)))) +// &mut u8 +AnyValueWrapper::Integer(IntegerValueWrapper::U8(Mutable(u8))) +// &mut any +AnyValueWrapper::AsSelf(Mutable(AnyValueContent::Integer(IntegerValueContent::U8(u8)))) +``` + +Essentially, outside of the `Form` we have `Wrapper` and inside the form we have `Mutable`. + +But then - how do we actually operate on this?? What operations for we supporty? + +Honestly, it's really hard. Would need to think about what operations can be performed etc. Even defining a leaf mapper was nigh-on impossible. + +Might come back to this at some point. + +## What's next? + +Let's put the "concepts" to-leaf on indefinite pause. It's definitely resulted in a lot of good cleaning up, BUT it's also created a lot of mess. + +Eventually we will need to clean it up: +* Better abstract the `IsArgument` impl +* Finish migrating from `bindings.rs` +* Finish removing `into_any_value()` etc +* Finish clearing up `arguments.rs` and `outputs.rs` + +We might well be left with only `BeOwned` and `BeRef`, `BeMut` - we'll see. + +See the below half-baked todo list for where this got to. + +--- + +But *first* I'd like to implement functions _without_ type annotations. Then if we add them, it would probably be easier for them to be runtime assertions rather than structural. + +## Half-finished to-do lists copied over (TODO: clean these up!) + +### Better handling of value sub-references + +- [x] Migrate from `Owned` having a span to a `Spanned` +- [ ] Implement and roll-out GATs + - [x] Initial shell implementation in `concepts` folder + - [x] Improved error handling in the macro (e.g. required arg after optional; no matching strongly-typed signature) + - [x] Separate Hierarchical and DynCompatible Forms + - [x] Improved macro support + - [x] Add source type name to type macro/s + - [x] Generate value kinds from the macros + - [x] Add (temporary) ability to link to TypeData and resolve methods from there + - [x] Then implement all the macros + - [x] And use that to generate AnyValueLeafKind from the new macros + - [x] Find a way to generate `from_source_name` - ideally efficiently + - [x] Add ability to implement IsIterable + - [x] `CastTarget` simply wraps `TypeKind` + - [x] Create a `Ref` and a `Mut` form + - [x] They won't implement `IsArgumentForm` + - [x] Create mappers and suitably generic `as_ref()` and `as_mut()` methods on `Actual` + - [x] Migrate method resolution to the trait macro properly + - [x] And property resolution `dyn TypeFeatureResolver` + - [x] Replace `impl TypeFeatureResolver for $type_def` + - [x] Remove `temp_type_data: $type_data:ident,` + - [x] Remove `HierarchicalTypeData` + - [ ] Stage 1 of the form migration: + - [x] Add temp blanket impl from `IntoValue` for `IntoValueContent` + - [x] Get rid of `BooleanValue` wrapper + - [x] Get rid of `StreamValue` wrapper + - [x] Get rid of `CharValue` wrapper + - [x] Replace `IntegerValue` with `type IntegerValue = IntegerContent<'static, BeOwned>` + - [x] Replace `FloatValue` with `type FloatValue = FloatContent<'static, BeOwned>` + - [x] Replace `Value` with `type Value = ValueContent<'static, BeOwned>` + - [x] Get rid of the `Actual` wrapper inside content + // --- + - [x] Trial getting rid of `Actual` completely? + - [x] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` + - [x] Create new branch + - [x] Resolve issue with `2.3` not resolving into `2f32` any more + - [x] .. same for int... + - [x] Update Value: + - [x] `ValueType` => `AnyType` + - [x] `type AnyValue = QqqOwned` + - [x] `type AnyValueRef = QqqRef` + - [x] `type AnyValueMut = QqqMut` + - [x] ... and move methods + - [x] Remove `OwnedValue` + - [x] Get rid of `Owned` + - [ ] Stage 2 of the form migration: + - [x] Attempt to improve mappers: + - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. + - [x] Finish mapper improvements + - [x] Migrate `self` + - [x] Consider if we even need `MapperOutput` or just some helper functions + - [x] Delay resolving the type kind into the error case when downcasting + - [x] Create macro to define inline mappers in various forms + - [x] Attempt to see if I can get rid of needing `leaf_to_content` and maybe `content_to_leaf` by exploiting a trick to bind associated types via a sub-trait (e.g. as I did with `IsValueContent::LeafType> + IsLeafValueContent`) + - [x] Pivot the argument resolution to work with either old or new values + - [ ] Remove as much from arguments.rs as possible + - [ ] Fix `todo!("Argument")` + - [ ] Pivot the return resolution to work with either old or new values + - [ ] Remove `ResolveAs` + - [ ] Look at better implementations of `FromArgument` + .. potentially via some new selector trait `IsResolvable` with a resolution strategy of `Hierarchichal` | `Dyn` | `Custom` + This will let us implement a more general resolution logic, and amalgamate argument parsing and downcast_resolve/dyn_resolve + - [ ] Reproduce `CopyOnWrite` + - [ ] Implement `TODO[concepts]: COPY ON WRITE MAPPING` + - [ ] `BeCopyOnWrite::owned()` / `shared_as_..` can go via `AnyLevelCopyOnWrite => into_copy_on_write` + - [ ] Reproduce `Shared` methods etc + - [ ] Reproduce `Mutable` methods etc + - [ ] Reproduce `Assignee` methods etc + - [ ] Change `CopyOnWrite` => `CopyOnWriteAnyValue` similarly with `Shared`, `Mutable` and `Assignee` + - [ ] Migrate `CopyOnWrite`, `Shared`, `Mutable`, `Assignee` + - [ ] `Shared` only works for leaves + - [ ] For specific parents, use e.g. `AnyValueShared` + - [ ] If you need something to apply across all leaves, implement it on some trait + `IsSelfSharedContent` depending/auto-implemented on `IsSelfValueContent` + - [ ] Same for `Mutable` and `Assignee` + - [ ] Stage 3 + - [ ] Migrate `CopyOnWrite` and relevant interconversions + - [ ] Finish migrating to new Argument/Returned resolution and delete old code + - [ ] Remove `impl_resolvable_argument_for` and `TODO[concepts]: Remove when we get rid of impl_resolvable_argument_for` + - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): + - [ ] Consider migrating LateBound and interconversions + - [ ] Argument, and `ArgumentOwnership` driven conversions into it + - [ ] Generate test over all value kinds which checks for: + - [ ] Maybe over TypeKinds with https://docs.rs/inventory/latest/inventory/ registered as a dev dependency + - [ ] Check for duplicate TypeKind registrations + - [ ] Source type has no spaces and is lower case, and is invertible + - [ ] Ancestor types agree with type kinds + - [ ] Clear up all `TODO[concepts]` + - [ ] Have variables store a `Referenceable` + - [ ] Separate methods and functions + - [ ] Add ability to add comments to types, methods/functions and operations, and generate docs from them + - [ ] Clean-up: + - [ ] Move indexing and property access (see e.g.`into_indexed`) etc to the type resolution system - this map allow us to remove + things like `AnyLevelCopyOnWrite` and the `TypeMapper` + - [ ] Most matching methods on `AnyValue` would be better as leaf methods \ No newline at end of file diff --git a/plans/TODO.md b/plans/TODO.md index 0ef1d7dd..89162502 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -200,136 +200,27 @@ First, read the @./2025-11-vision.md ## Better handling of value sub-references -- [x] Migrate from `Owned` having a span to a `Spanned` -- [ ] Implement and roll-out GATs - - [x] Initial shell implementation in `concepts` folder - - [x] Improved error handling in the macro (e.g. required arg after optional; no matching strongly-typed signature) - - [x] Separate Hierarchical and DynCompatible Forms - - [x] Improved macro support - - [x] Add source type name to type macro/s - - [x] Generate value kinds from the macros - - [x] Add (temporary) ability to link to TypeData and resolve methods from there - - [x] Then implement all the macros - - [x] And use that to generate AnyValueLeafKind from the new macros - - [x] Find a way to generate `from_source_name` - ideally efficiently - - [x] Add ability to implement IsIterable - - [x] `CastTarget` simply wraps `TypeKind` - - [x] Create a `Ref` and a `Mut` form - - [x] They won't implement `IsArgumentForm` - - [x] Create mappers and suitably generic `as_ref()` and `as_mut()` methods on `Actual` - - [x] Migrate method resolution to the trait macro properly - - [x] And property resolution `dyn TypeFeatureResolver` - - [x] Replace `impl TypeFeatureResolver for $type_def` - - [x] Remove `temp_type_data: $type_data:ident,` - - [x] Remove `HierarchicalTypeData` - - [ ] Stage 1 of the form migration: - - [x] Add temp blanket impl from `IntoValue` for `IntoValueContent` - - [x] Get rid of `BooleanValue` wrapper - - [x] Get rid of `StreamValue` wrapper - - [x] Get rid of `CharValue` wrapper - - [x] Replace `IntegerValue` with `type IntegerValue = IntegerContent<'static, BeOwned>` - - [x] Replace `FloatValue` with `type FloatValue = FloatContent<'static, BeOwned>` - - [x] Replace `Value` with `type Value = ValueContent<'static, BeOwned>` - - [x] Get rid of the `Actual` wrapper inside content - // --- - - [x] Trial getting rid of `Actual` completely? - - [x] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` - - [x] Create new branch - - [x] Resolve issue with `2.3` not resolving into `2f32` any more - - [x] .. same for int... - - [x] Update Value: - - [x] `ValueType` => `AnyType` - - [x] `type AnyValue = QqqOwned` - - [x] `type AnyValueRef = QqqRef` - - [x] `type AnyValueMut = QqqMut` - - [x] ... and move methods - - [x] Remove `OwnedValue` - - [x] Get rid of `Owned` - - [ ] Stage 2 of the form migration: - - [x] Attempt to improve mappers: - - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. - - [x] Finish mapper improvements - - [x] Migrate `self` - - [x] Consider if we even need `MapperOutput` or just some helper functions - - [x] Delay resolving the type kind into the error case when downcasting - - [x] Create macro to define inline mappers in various forms - - [x] Attempt to see if I can get rid of needing `leaf_to_content` and maybe `content_to_leaf` by exploiting a trick to bind associated types via a sub-trait (e.g. as I did with `IsValueContent::LeafType> + IsLeafValueContent`) - - [x] Pivot the argument resolution to work with either old or new values - - [ ] Remove as much from arguments.rs as possible - - [ ] Fix `todo!("Argument")` - - [ ] Pivot the return resolution to work with either old or new values - - [ ] Remove `ResolveAs` - - [ ] Look at better implementations of `FromArgument` - .. potentially via some new selector trait `IsResolvable` with a resolution strategy of `Hierarchichal` | `Dyn` | `Custom` - This will let us implement a more general resolution logic, and amalgamate argument parsing and downcast_resolve/dyn_resolve - - [ ] Reproduce `CopyOnWrite` - - [ ] Implement `TODO[concepts]: COPY ON WRITE MAPPING` - - [ ] `BeCopyOnWrite::owned()` / `shared_as_..` can go via `AnyLevelCopyOnWrite => into_copy_on_write` - - [ ] Reproduce `Shared` methods etc - - [ ] Reproduce `Mutable` methods etc - - [ ] Reproduce `Assignee` methods etc - - [ ] Change `CopyOnWrite` => `CopyOnWriteAnyValue` similarly with `Shared`, `Mutable` and `Assignee` - - [ ] Migrate `CopyOnWrite`, `Shared`, `Mutable`, `Assignee` - - [ ] `Shared` only works for leaves - - [ ] For specific parents, use e.g. `AnyValueShared` - - [ ] If you need something to apply across all leaves, implement it on some trait - `IsSelfSharedContent` depending/auto-implemented on `IsSelfValueContent` - - [ ] Same for `Mutable` and `Assignee` - - [ ] Stage 3 - - [ ] Migrate `CopyOnWrite` and relevant interconversions - - [ ] Finish migrating to new Argument/Returned resolution and delete old code - - [ ] Remove `impl_resolvable_argument_for` and `TODO[concepts]: Remove when we get rid of impl_resolvable_argument_for` - - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): - - [ ] Consider migrating LateBound and interconversions - - [ ] Argument, and `ArgumentOwnership` driven conversions into it - - [ ] Generate test over all value kinds which checks for: - - [ ] Maybe over TypeKinds with https://docs.rs/inventory/latest/inventory/ registered as a dev dependency - - [ ] Check for duplicate TypeKind registrations - - [ ] Source type has no spaces and is lower case, and is invertible - - [ ] Ancestor types agree with type kinds - - [ ] Clear up all `TODO[concepts]` - - [ ] Have variables store a `Referenceable` - - [ ] Separate methods and functions - - [ ] Add ability to add comments to types, methods/functions and operations, and generate docs from them - - [ ] Clean-up: - - [ ] Move indexing and property access (see e.g.`into_indexed`) etc to the type resolution system - this map allow us to remove - things like `AnyLevelCopyOnWrite` and the `TypeMapper` - - [ ] Most matching methods on `AnyValue` would be better as leaf methods +Moved to [2026-01-types-and-forms.md](./2026-01-types-and-forms.md). ## Methods and closures -- [ ] Improved Shared/Mutable handling - See the `Better handling of value sub-references` section. The key requirement is we need to allow a `Shared` to map back to a `Shared`. Moving the "sharedness" to the leaves permits this. - * A function specifies the bindings of its variables - * If we have `my_len = |x: &array| x.len()` and invoke it as `my_len(a.b)` then - when I invoke it, I need to end up with the variable `x := &a.b` - * This is a problem - if we imagine changing what can be stored in a variable to - the following, then it's clear that we need some way to have a `SharedValue` which - has an outer-enum instead of an inner-enum. - * We also need to think about how things like `IterableValue` works. Perhaps it's like an interface, - and so defined via `Box` / `Ref` etc? +- [ ] Consider pre-requisite work on [2026-01-types-and-forms.md](./2026-01-types-and-forms.md) for type annotations. Instead, let's move forward without support for specific types for now. To start, let's just support: `x` or `x: any`; `x: &any` and `x: &mut any`. +- [ ] Change bindings (currently just variables) to be able to store any of the following: (nb we still restrict variables to be owned for now). ```rust -// Before enum VariableContent { - Owned(Rc>), - Shared(SharedSubRcRefCell), - Mutable(MutableSubRcRefCell), -} -// After -enum VariableContent { - Owned(ValueReferencable), - Shared(ValueRef<'static>), // 'static => only SharedSubRcRefCell, no actual refs - Mutable(ValueMut<'static>), // 'static => only MutableSubRcRefCell, no refs + Owned(Referenceable), + Shared(Shared), + Mutable(Mutable), } ``` - [ ] Introduce basic function values * Value type function `let my_func = |x, y, z| { ... };` - * Parameters can be `x` (Owned), `&x` (Shared) or `&mut x` (Mutable), shorthand for - e.g. `x: &value` + * Parameters can be `x` / `x: any` (Owned), `x: &any` (Shared) or `x: &mut any` (Mutable). * To start with, they are not closures (i.e. they can't capture any outer variables) - - [ ] Break/continue label resolution in functions/closures - * Functions and closures must resolve break/continue labels statically - * Break and continue statements should not leak out of function boundaries - * This needs to be validated during the control flow pass +- [ ] Break/continue label resolution in functions/closures + * Functions and closures must resolve break/continue labels statically + * Break and continue statements should not leak out of function boundaries + * This needs to be validated during the control flow pass - [ ] New node extension in the expression parser: invocation `(...)` - [ ] Closures * A function may capture variable bindings from the parent scope, these are converted into a `VariableBinding::Closure()` @@ -616,98 +507,6 @@ Also: - [ ] Check all `#[allow(unused)]` and remove any which aren't needed We can use `_xyz: Unused` in some places to reduce the size of types. -## Better handling of value sub-references - -### OPTION 1 - Enums with GATs - -> [!NOTE] -> See `sandbox/gat_value.rs` for playing around with this idea - -Returning/passing refs of sub-values requires taking the enum outside of the reference, -i.e. some `ValueRef<'a>`, perhaps similar to `IterableRef`? - -```rust -enum ValueRef<'a> { - Integer(IntegerRef<'a>), - Object(AnyRef<'a, ObjectValue>), - // ... -} -``` - -We could even consider abusing GATs further, to define the structures only once: -```rust -trait OwnershipSelector { - type Leaf; -} -struct IsOwned; -impl OwnershipSelector for IsOwned { - type Leaf = T; -} -// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. -struct IsReferencable; -impl OwnershipSelector for IsReferencable { - type Leaf = Rc>; -} -struct IsRef<'a>; -impl<'a> OwnershipSelector for IsRef<'a> { - type Leaf = AnyRef<'a, T>; -} -struct IsMut<'a>; -impl<'a> OwnershipSelector for IsMut<'a> { - type Leaf = AnyMutRef<'a, T>; -} - -enum ValueWhich { - Integer(IntegerStructure), - Object(H::Leaf::), -// ... -} - -type Value = ValueWhich; -type ValueReferencable = ValueWhich; -type ValueRef<'a> = ValueWhich>; -type ValueMut<'a> = ValueWhich>; -``` - -- [ ] Trial if `Shared` can actually store an `ValueRef<'a>` (which just stores `&'a`, not the `Ref` variable)... - * This could be done by adding GATs (raising MSRV to 1.65) so that TypeData can have a `Ref<'T>`, - with `Value::Ref<'T> = ValueRef<'T>`... although we only really need GATs for allowing arbitrary - references, not just static `SharedSubRcRefCell` from Shared - * And then `Shared<'t, T>` can wrap a `::Type::Ref<'t, T>` (in the file, this can be emplaced as a `HasRefType` trait, which can be blanket implemeted for types implementing `..Target`). - * This would mean e.g. `Shared` could wrap a `&str`. - * 6 months later I'm not sure what this means: - * And then have a `AdvancedCellRef` store a `::Type::Ref<'T>` which can be owned and we can manually call increase strong count etc on the `RefCell`. - * To implement `AdvancedCellRef::map`, we'll need `TypeData::Ref<'T>` to implement Target in a self-fulfilling way. (i.e. `HasRefType { type Ref<'a>: HasRefParent }`, `HasRefParent { type Parent: HasRefType })`) - * If this works, we can replace our `Ref` with `T: HasRefType` - * Migrate `IterableRef` - -### OPTION 2 - Box + Dyn - -> [!NOTE] -> See `sandbox/dyn_value.rs` for playing around with this idea - -If we can make this work, it's perhaps slightly less performant (I wonder how much?) but would probably compile faster, and be less tied to structure; so support. - -See below for some rough ideas. - -For owned values: -* `Box` with `IsValue: Any` (maybe using https://docs.rs/downcast-rs/latest/downcast_rs/ to avoid `Any`) -* From that, `IsValue` allows resolving `&'static TypeData` -* Which can expose methods such as `as_integer(Box) -> Option>` - * Which can downcast `Box` to specific value, e.g. `Box` - * Then can upcast that to a specific trait such as `Box` or `Box` - -For reference values: -* `AnyRef` -* `TypeData` can expose methods such as `as_integer_ref(AnyRef) -> Option>` - .. using `downcast_ref` and then upcasting... - ... I wonder if this can be automatic. `if Self::Value : IsInteger` then we implement with a cast, if not? - -For mutable values: -* `AnyRefMut` -* `TypeData` can expose methods such as `as_integer_mut(AnyRefMut) -> Option>` - .. using `downcast_mut` and then upcasting. - ## Cloning * Consider making Iterator non-clonable (which will unlock many more easy lazy implementations, of e.g. `take` using the non-clonable `Take`, and similar for other mapped iterators), i.e. `ExpressionValue` has a manual `clone() -> ExecutionResult` - this will simplify some things. But then, `to_string()` would want to take a `CopyOnWrite` so that where we clone, the iterator can potentially take owned, attempt clone, else error. @@ -789,23 +588,23 @@ This means that this is low-clone: ## Value expansions [OPTIONAL] -Consider: -* Do we want some kind of slice object? (see `TODO[range-refactor]`) - * We can make `ExpressionValue` deref into `ExpressionRef`, e.g. `ExpressionRef::Array()` - * Then we can make `SharedValue(Ref)`, which can be constructed from a `Ref` with a map! - * And similarly `MutableValue(RefMut)` -* Using ArgumentValue in place of ExpressionValue e.g. inside arrays / objects, so that we can destructure `let (x, y) = (a, b)` without clone/take - * But then we end up with nested references which can be confusing! - * CONCLUSION: Maybe we don't want this - to destructure it needs to be owned anyway? +* Consider somehow adding some kind of slice object? (see `TODO[slice-supprt]`) + * To do this, we basically need to have a leaf-kind of `Mutable`, so we can map an + `arr[0..2]` to a `Mutable<[AnyValue]>` - but this then can't be converted back to + a `Mutable`. This is then a binding structurally of type `&mut slice`. + * Supporting structurally type-restricted bindings is ... complicated ... + And whilst the machinery for leaf-restricted bindings is in place, their exposure and + inter-op with any-bindings is very much not (as of Jan 26). + * See [2026-01-types-and-forms.md](./2026-01-types-and-forms.md) for more details. * Consider whether to expand to storing `ArgumentValue` or `CopyOnWriteValue` in variables instead of `OwnedValue`? - => The main issue is if it interferes with taking mutable references, but it's possibly OK, would need to see if it's a confusing problem in practice... (e.g. `let b = a[0]; a.push(1)` if `b` is a reference to `a[0]` then this is a problem when we push to `a`) - => If a mutable reference is created and there are pending references, the variable data RefCell could be replaced with a cloned value and then mutated... But this can be more expensive, because e.g. `let b = a[0]; a.push(1)` results in the whole array `a` being copied in the `CoW` case; but only the `a[0]` being cloned in the "clone on assign" case. - => Maybe we just stick to assignments being Owned/Cloned as currently + - The main issue is if it interferes with taking mutable references, but it's possibly OK, would need to see if it's a confusing problem in practice... (e.g. `let b = a[0]; a.push(1)` if `b` is a reference to `a[0]` then this is a problem when we push to `a`) + - If a mutable reference is created and there are pending references, the variable data RefCell could be replaced with a cloned value and then mutated... But this can be more expensive, because e.g. `let b = a[0]; a.push(1)` results in the whole array `a` being copied in the `CoW` case; but only the `a[0]` being cloned in the "clone on assign" case. + - Maybe we just stick to assignments being Owned/Cloned as currently * Support `#(x[..])` syntax for indexing streams, like with arrays - * `#(x[0])` returns the value at that position of the stream (using `INFER_TOKEN_TREE`) - * `#(x[0..3])` returns a TokenStream - * `#(x[0..=3])` returns a TokenStream + * `#(x[0])` returns the value at that position of the stream (using `INFER_TOKEN_TREE`) + * `#(x[0..3])` returns a TokenStream + * `#(x[0..=3])` returns a TokenStream -------------------------------------------------------------------------------- diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 033e6cba..c0debd2a 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -1146,7 +1146,6 @@ impl EvaluationFrame for RangeBuilder { context: ValueContext, Spanned(value, _span): Spanned, ) -> ExecutionResult { - // TODO[range-refactor]: Change to not always clone the value let value = value.expect_owned(); Ok(match (self.state, self.range_limits) { (RangePath::OnLeftBranch { right: Some(right) }, _) => { diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 25527850..eaa02b03 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -70,7 +70,7 @@ impl ArrayValue { &mut self.items[index] } AnyValueContent::Range(..) => { - // Temporary until we add slice types - we error here + // TODO[slice-support] Temporary until we add slice types - we error here return span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]"); } _ => return span_range.type_err("The index must be an integer or a range"), @@ -88,7 +88,7 @@ impl ArrayValue { &self.items[index] } AnyValueContent::Range(..) => { - // Temporary until we add slice types - we error here + // TODO[slice-support] Temporary until we add slice types - we error here return span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]"); } _ => return span_range.type_err("The index must be an integer or a range"),