diff --git a/plans/TODO.md b/plans/TODO.md index 7776dff5..ba9b359a 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -305,13 +305,25 @@ The issue is that we get a variable stored as `arr := DisabledMutable` and then - [x] Emplacing - [x] Map, Try map - [x] Ability to map deeper. Should take a `PathExtension` and a new span. -- [ ] Try replacing existing RefCell based abstractions with new custom dynamic references, - and report on what breaks: - - `pub(crate) type Referenceable = Rc>;` becomes `pub(crate) type Referenceable = dynamic_references::Referenceable` - - `Shared` / `SharedSubRcRefCell` - both become type aliases for `SharedReference` - - `DisabledShared` becomes `InactiveSharedReference` - - `Mutable` / `MutableSubRcRefCell` - becomes type alias for `MutableReference` - - `DisabledMutable` becomes `InactiveMutableReference` +- [x] Try replacing existing RefCell based abstractions with new custom dynamic references, and report on what breaks: + - `pub(crate) type Referenceable = Rc>;` becomes `pub(crate) type Referenceable = dynamic_references::Referenceable` + - `Shared` / `SharedSubRcRefCell` - both become type aliases for `SharedReference` + - `DisabledShared` becomes `InactiveSharedReference` + - `Mutable` / `MutableSubRcRefCell` - becomes type alias for `MutableReference` + - `DisabledMutable` becomes `InactiveMutableReference` +- [ ] Replace all the aliases: + - Remove `QqqShared`, `QqqMutable`, `SharedValue`, `AssigneeValue` + - Move `type Shared` and `type Mutable` alongside `SharedReference` / `MutableReference` + - Remove references to `MutableReference` and `SharedReference` outside these aliases + - Rename `SharedReference -> Shared` and `MutableReference -> Mutable` + - Rename `DisabledShared` -> `InactiveShared` and same for `Mutable` + - Remove `type AssigneeValue` and `type SharedValue` + - Move `Assignee` out of bindings. To e.g. `dynamic_references` + - See what else can be deleted from `bindings.rs` + - Rename `PathExtension::Tightened` to `PathExtension::TypeNarrowed` + - Rename `DisabledArgumentValue` and `DisabledCopyOnWrite` to + `Inactive__` and their method from `enable` to `activate` and ditto + with `disable -> deactivate` ### Other ideas diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index 641cbcd0..16ae7a21 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -2,8 +2,7 @@ use super::*; /// Shorthand for representing the form F of a type T with a particular lifetime 'a. pub(crate) type Content<'a, T, F> = ::Content<'a, F>; -pub(crate) type DynContent<'a, D, F> = - ::DynLeaf<'a, ::DynContent>; +pub(crate) type DynContent<'a, D, F> = ::DynLeaf<'a, D>; /// For types which have an associated value (type and form) pub(crate) trait IsValueContent { @@ -69,18 +68,6 @@ where { self.upcast() } - - fn into_referenceable(self) -> Content<'a, Self::Type, BeReferenceable> - where - Self: IsValueContent
, - { - map_via_leaf! { - input: (Content<'a, Self::Type, Self::Form>) = self.into_content(), - fn map_leaf(leaf) -> (Content<'a, T, BeReferenceable>) { - Rc::new(RefCell::new(leaf)) - } - } - } } impl<'a, C> Spanned @@ -226,7 +213,7 @@ impl< type ValueType = T; const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; fn from_argument(Spanned(value, span_range): Spanned) -> FunctionResult { - let ownership_mapped = F::from_argument_value(value)?; + let ownership_mapped = F::from_argument_value(Spanned(value, span_range))?; let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; Ok(X::from_content(type_mapped)) } diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 6337ce19..a7113215 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -18,6 +18,13 @@ pub(crate) trait IsHierarchicalForm: IsForm { type Leaf<'a, T: IsLeafType>: IsValueContent + IntoValueContent<'a> + FromValueContent<'a>; + + /// Proof of covariance: shrinks the lifetime of a leaf from `'a` to `'b`. + /// Implementors should use the identity function `{ leaf }` as the body. + /// This will only compile if the leaf type is genuinely covariant in `'a`. + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b; } pub(crate) trait LeafAsRefForm: IsHierarchicalForm { @@ -53,20 +60,21 @@ 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>; + type DynLeaf<'a, D: IsDynType>; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + leaf: Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn; + T::Leaf: CastDyn; } pub(crate) trait MapFromArgument: IsHierarchicalForm { const ARGUMENT_OWNERSHIP: ArgumentOwnership; - fn from_argument_value(value: ArgumentValue) - -> FunctionResult>; + fn from_argument_value( + value: Spanned, + ) -> FunctionResult>; } pub(crate) trait MapIntoReturned: IsHierarchicalForm { diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index 43f623e9..a4761ec8 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -22,20 +22,34 @@ pub(crate) struct BeAnyMut; impl IsForm for BeAnyMut {} impl IsHierarchicalForm for BeAnyMut { type Leaf<'a, T: IsLeafType> = AnyMut<'a, T::Leaf>; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl IsDynCompatibleForm for BeAnyMut { - type DynLeaf<'a, D: 'static + ?Sized> = AnyMut<'a, D>; + type DynLeaf<'a, D: IsDynType> = AnyMut<'a, D::DynContent>; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + Spanned(leaf, span): Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn, + T::Leaf: CastDyn, { - leaf.replace(|content, emplacer| match ::map_mut(content) { - Ok(mapped) => Ok(emplacer.emplace(mapped)), - Err(this) => Err(emplacer.emplace(this)), + leaf.emplace_map(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => { + // SAFETY: PathExtension::Tightened correctly describes type narrowing + let mapped_mut = unsafe { + MappedMut::new(mapped, PathExtension::Tightened(D::type_kind()), span) + }; + Ok(emplacer.emplace(mapped_mut)) + } + Err(_this) => Err(emplacer.revert()), }) } } @@ -56,11 +70,12 @@ impl MapFromArgument for BeAnyMut { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; fn from_argument_value( - value: ArgumentValue, + Spanned(value, span): Spanned, ) -> FunctionResult> { - Ok(value - .expect_mutable() - .0 - .replace(|inner, emplacer| inner.as_mut_value().into_mutable_any_mut(emplacer))) + Ok(value.expect_mutable().emplace_map(|inner, emplacer| { + inner + .as_mut_value() + .into_mutable_any_mut(emplacer, Some(span)) + })) } } diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index b813264b..51407b86 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -23,20 +23,34 @@ impl IsForm for BeAnyRef {} impl IsHierarchicalForm for BeAnyRef { type Leaf<'a, T: IsLeafType> = crate::internal_prelude::AnyRef<'a, T::Leaf>; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl IsDynCompatibleForm for BeAnyRef { - type DynLeaf<'a, D: 'static + ?Sized> = crate::internal_prelude::AnyRef<'a, D>; + type DynLeaf<'a, D: IsDynType> = crate::internal_prelude::AnyRef<'a, D::DynContent>; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + Spanned(leaf, span): Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn, + T::Leaf: CastDyn, { - leaf.replace(|content, emplacer| match ::map_ref(content) { - Ok(mapped) => Ok(emplacer.emplace(mapped)), - Err(this) => Err(emplacer.emplace(this)), + leaf.emplace_map(|content, emplacer| match ::map_ref(content) { + Ok(mapped) => { + // SAFETY: PathExtension::Tightened correctly describes type narrowing + let mapped_ref = unsafe { + MappedRef::new(mapped, PathExtension::Tightened(D::type_kind()), span) + }; + Ok(emplacer.emplace(mapped_ref)) + } + Err(_this) => Err(emplacer.revert()), }) } } @@ -51,11 +65,12 @@ impl MapFromArgument for BeAnyRef { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; fn from_argument_value( - value: ArgumentValue, + Spanned(value, span): Spanned, ) -> FunctionResult> { - Ok(value - .expect_shared() - .0 - .replace(|inner, emplacer| inner.as_ref_value().into_shared_any_ref(emplacer))) + Ok(value.expect_shared().emplace_map(|inner, emplacer| { + inner + .as_ref_value() + .into_shared_any_ref(emplacer, Some(span)) + })) } } diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index f7cafdfc..ce455b06 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -31,13 +31,21 @@ impl IsForm for BeArgument {} impl IsHierarchicalForm for BeArgument { type Leaf<'a, T: IsLeafType> = Argument; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl MapFromArgument for BeArgument { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; fn from_argument_value( - value: ArgumentValue, + Spanned(value, _span): Spanned, ) -> FunctionResult> { todo!("Argument") } diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 489bb712..37a8744c 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) MutableReference); impl IsValueContent for QqqAssignee { type Type = L::Type; @@ -25,21 +25,35 @@ impl IsForm for BeAssignee {} impl IsHierarchicalForm for BeAssignee { type Leaf<'a, T: IsLeafType> = QqqAssignee; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl IsDynCompatibleForm for BeAssignee { - type DynLeaf<'a, D: 'static + ?Sized> = QqqAssignee; + type DynLeaf<'a, D: IsDynType> = QqqAssignee; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + Spanned(leaf, span): Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn, + T::Leaf: CastDyn, { leaf.0 - .replace(|content, emplacer| match ::map_mut(content) { - Ok(mapped) => Ok(QqqAssignee(emplacer.emplace(mapped))), - Err(this) => Err(QqqAssignee(emplacer.emplace(this))), + .emplace_map(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => { + // SAFETY: PathExtension::Tightened correctly describes type narrowing + let mapped_mut = unsafe { + MappedMut::new(mapped, PathExtension::Tightened(D::type_kind()), span) + }; + Ok(QqqAssignee(emplacer.emplace(mapped_mut))) + } + Err(_this) => Err(QqqAssignee(emplacer.revert())), }) } } @@ -61,7 +75,7 @@ impl MapFromArgument for BeAssignee { ArgumentOwnership::Assignee { auto_create: false }; fn from_argument_value( - value: ArgumentValue, + Spanned(value, _span): Spanned, ) -> FunctionResult> { 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 8a644ddb..742d01bc 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -34,6 +34,14 @@ impl IsForm for BeCopyOnWrite {} impl IsHierarchicalForm for BeCopyOnWrite { type Leaf<'a, T: IsLeafType> = QqqCopyOnWrite; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl BeCopyOnWrite { @@ -75,7 +83,7 @@ impl MapFromArgument for BeCopyOnWrite { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; fn from_argument_value( - value: ArgumentValue, + Spanned(value, span): Spanned, ) -> FunctionResult> { match value.expect_copy_on_write().inner { CopyOnWriteInner::Owned(owned) => Ok(BeCopyOnWrite::new_owned(owned)), diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs index 0395e1ca..67ba3bc1 100644 --- a/src/expressions/concepts/forms/late_bound.rs +++ b/src/expressions/concepts/forms/late_bound.rs @@ -44,6 +44,14 @@ impl IsForm for BeLateBound {} impl IsHierarchicalForm for BeLateBound { type Leaf<'a, T: IsLeafType> = QqqLateBound; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } pub(crate) struct QqqLateBoundOwned { diff --git a/src/expressions/concepts/forms/mod.rs b/src/expressions/concepts/forms/mod.rs index 3f42e697..17102ad6 100644 --- a/src/expressions/concepts/forms/mod.rs +++ b/src/expressions/concepts/forms/mod.rs @@ -9,7 +9,6 @@ mod copy_on_write; mod late_bound; mod mutable; mod owned; -mod referenceable; mod shared; mod simple_mut; mod simple_ref; @@ -22,7 +21,6 @@ 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::*; 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 d8c79110..40e57ca5 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -1,18 +1,27 @@ use super::*; -pub(crate) type QqqMutable = MutableSubRcRefCell; +pub(crate) type QqqMutable = MutableReference; -impl IsValueContent for QqqMutable { - type Type = L::Type; +impl IsValueContent for QqqMutable { + type Type = X::Type; type Form = BeMutable; } -impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqMutable { +impl<'a, X: IsValueContent> IntoValueContent<'a> for QqqMutable +where + X: 'static, + X::Type: IsHierarchicalType = X>, + X::Form: IsHierarchicalForm, + X::Form: LeafAsMutForm, +{ fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - self + self.emplace_map(|inner, emplacer| inner.as_mut_value().into_mutable(emplacer, None)) } } +// Note we can't implement this more widely than leaves. +// This is because e.g. Content has Mutable() in its leaves, +// This can't be mapped back to having Mutable(Content). impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqMutable { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { content @@ -25,20 +34,34 @@ impl IsForm for BeMutable {} impl IsHierarchicalForm for BeMutable { type Leaf<'a, T: IsLeafType> = QqqMutable; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl IsDynCompatibleForm for BeMutable { - type DynLeaf<'a, D: 'static + ?Sized> = QqqMutable; + type DynLeaf<'a, D: IsDynType> = QqqMutable; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + Spanned(leaf, span): Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn, + T::Leaf: CastDyn, { - leaf.replace(|content, emplacer| match ::map_mut(content) { - Ok(mapped) => Ok(emplacer.emplace(mapped)), - Err(this) => Err(emplacer.emplace(this)), + leaf.emplace_map(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => { + // SAFETY: PathExtension::Tightened correctly describes type narrowing + let mapped_mut = unsafe { + MappedMut::new(mapped, PathExtension::Tightened(D::type_kind()), span) + }; + Ok(emplacer.emplace(mapped_mut)) + } + Err(_) => Err(emplacer.revert()), }) } } @@ -59,9 +82,11 @@ impl MapFromArgument for BeMutable { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; fn from_argument_value( - value: ArgumentValue, + Spanned(value, span): Spanned, ) -> FunctionResult> { - Ok(value.expect_mutable().into_content()) + Ok(value + .expect_mutable() + .emplace_map(|inner, emplacer| inner.as_mut_value().into_mutable(emplacer, Some(span)))) } } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 09873161..5a575a03 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -18,16 +18,24 @@ impl IsForm for BeOwned {} impl IsHierarchicalForm for BeOwned { type Leaf<'a, T: IsLeafType> = T::Leaf; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl IsDynCompatibleForm for BeOwned { - type DynLeaf<'a, D: 'static + ?Sized> = Box; + type DynLeaf<'a, D: IsDynType> = Box; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + Spanned(leaf, _span): Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn, + T::Leaf: CastDyn, { ::map_boxed(Box::new(leaf)).map_err(|boxed| *boxed) } @@ -49,7 +57,7 @@ impl MapFromArgument for BeOwned { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; fn from_argument_value( - value: ArgumentValue, + Spanned(value, _span): Spanned, ) -> FunctionResult> { Ok(value.expect_owned()) } diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs deleted file mode 100644 index 47b7b622..00000000 --- a/src/expressions/concepts/forms/referenceable.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; - -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> { - self - } -} - -impl<'a, L: IsValueLeaf> FromValueContent<'a> for Referenceable { - fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { - 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. -/// -/// 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 {} - -impl IsHierarchicalForm for BeReferenceable { - type Leaf<'a, T: IsLeafType> = Referenceable; -} diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index e9a21302..5015adf5 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -1,18 +1,27 @@ use super::*; -pub(crate) type QqqShared = SharedSubRcRefCell; +pub(crate) type QqqShared = SharedReference; -impl IsValueContent for QqqShared { - type Type = L::Type; +impl IsValueContent for QqqShared { + type Type = X::Type; type Form = BeShared; } -impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqShared { +impl<'a, X: IsValueContent> IntoValueContent<'a> for QqqShared +where + X: 'static, + X::Type: IsHierarchicalType = X>, + X::Form: IsHierarchicalForm, + X::Form: LeafAsRefForm, +{ fn into_content(self) -> Content<'a, Self::Type, Self::Form> { - self + self.emplace_map(|inner, emplacer| inner.as_ref_value().into_shared(emplacer, None)) } } +// Note we can't implement this more widely than leaves. +// This is because e.g. Content has Shared() in its leaves, +// This can't be mapped back to having Shared(Content). impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqShared { fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { content @@ -25,20 +34,34 @@ impl IsForm for BeShared {} impl IsHierarchicalForm for BeShared { type Leaf<'a, T: IsLeafType> = QqqShared; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl IsDynCompatibleForm for BeShared { - type DynLeaf<'a, D: 'static + ?Sized> = QqqShared; + type DynLeaf<'a, D: IsDynType> = QqqShared; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + Spanned(leaf, span): Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn, + T::Leaf: CastDyn, { - leaf.replace(|content, emplacer| match ::map_ref(content) { - Ok(mapped) => Ok(emplacer.emplace(mapped)), - Err(this) => Err(emplacer.emplace(this)), + leaf.emplace_map(|content, emplacer| match ::map_ref(content) { + Ok(mapped) => { + // SAFETY: PathExtension::Tightened correctly describes type narrowing + let mapped_ref = unsafe { + MappedRef::new(mapped, PathExtension::Tightened(D::type_kind()), span) + }; + Ok(emplacer.emplace(mapped_ref)) + } + Err(_) => Err(emplacer.revert()), }) } } @@ -53,9 +76,11 @@ impl MapFromArgument for BeShared { const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; fn from_argument_value( - value: ArgumentValue, + Spanned(value, span): Spanned, ) -> FunctionResult> { - Ok(value.expect_shared().into_content()) + Ok(value + .expect_shared() + .emplace_map(|inner, emplacer| inner.as_ref_value().into_shared(emplacer, Some(span)))) } } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs index fcd4bbe4..ff447af5 100644 --- a/src/expressions/concepts/forms/simple_mut.rs +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -26,16 +26,24 @@ impl IsForm for BeMut {} impl IsHierarchicalForm for BeMut { type Leaf<'a, T: IsLeafType> = &'a mut T::Leaf; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl IsDynCompatibleForm for BeMut { - type DynLeaf<'a, D: 'static + ?Sized> = &'a mut D; + type DynLeaf<'a, D: IsDynType> = &'a mut D::DynContent; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + Spanned(leaf, _span): Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn, + T::Leaf: CastDyn, { ::map_mut(leaf) } @@ -52,15 +60,17 @@ where Self: IsValueContent, Self::Type: IsHierarchicalType = Self>, { - fn into_mutable<'b, T: 'static>( + fn into_mutable<'o, 'b, T: 'static>( self, emplacer: &'b mut MutableEmplacer<'a, T>, - ) -> Content<'static, Self::Type, BeMutable> + span: Option, + ) -> Content<'o, Self::Type, BeMutable> where Self: Sized, { struct __InlineMapper<'b, 'e2, X: 'static> { emplacer: &'b mut MutableEmplacer<'e2, X>, + span: Option, } impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeMutable>; @@ -75,23 +85,34 @@ where self, leaf: ::Leaf<'l, T>, ) -> Self::Output<'l, T> { - // SAFETY: 'l = 'a = 'e so this is valid - unsafe { self.emplacer.emplace_unchecked(leaf) } + // SAFETY: 'l = 'a = 'e2 so the lifetime transmute is valid, and + // PathExtension::Tightened correctly describes type narrowing + let mapped = unsafe { + MappedMut::new_unchecked( + leaf, + PathExtension::Tightened(T::type_kind()), + self.span, + ) + }; + self.emplacer.emplace(mapped) } }; - let __mapper = __InlineMapper { emplacer }; - ::map_with::(__mapper, self) + let __mapper = __InlineMapper { emplacer, span }; + let content = ::map_with::(__mapper, self); + ::covariant::(content) } - fn into_assignee<'b, T: 'static>( + fn into_assignee<'o, 'b, T: 'static>( self, emplacer: &'b mut MutableEmplacer<'a, T>, - ) -> Content<'static, Self::Type, BeAssignee> + span: Option, + ) -> Content<'o, Self::Type, BeAssignee> where Self: Sized, { struct __InlineMapper<'b, 'e2, X: 'static> { emplacer: &'b mut MutableEmplacer<'e2, X>, + span: Option, } impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAssignee>; @@ -106,23 +127,34 @@ where self, leaf: ::Leaf<'l, T>, ) -> Self::Output<'l, T> { - // SAFETY: 'l = 'a = 'e so this is valid - unsafe { QqqAssignee(self.emplacer.emplace_unchecked(leaf)) } + // SAFETY: 'l = 'a = 'e2 so the lifetime transmute is valid, and + // PathExtension::Tightened correctly describes type narrowing + let mapped = unsafe { + MappedMut::new_unchecked( + leaf, + PathExtension::Tightened(T::type_kind()), + self.span, + ) + }; + QqqAssignee(self.emplacer.emplace(mapped)) } }; - let __mapper = __InlineMapper { emplacer }; - ::map_with::(__mapper, self) + let __mapper = __InlineMapper { emplacer, span }; + let content = ::map_with::(__mapper, self); + ::covariant::(content) } - fn into_mutable_any_mut<'b, T: 'static>( + fn into_mutable_any_mut<'o, 'b, T: 'static>( self, emplacer: &'b mut MutableEmplacer<'a, T>, - ) -> Content<'static, Self::Type, BeAnyMut> + span: Option, + ) -> Content<'o, Self::Type, BeAnyMut> where Self: Sized, { struct __InlineMapper<'b, 'e2, X: 'static> { emplacer: &'b mut MutableEmplacer<'e2, X>, + span: Option, } impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAnyMut>; @@ -137,12 +169,22 @@ where 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() } + // SAFETY: 'l = 'a = 'e2 so the lifetime transmute is valid, and + // PathExtension::Tightened correctly describes type narrowing + let mapped = unsafe { + MappedMut::new_unchecked( + leaf, + PathExtension::Tightened(T::type_kind()), + self.span, + ) + }; + let mutable_ref: MutableReference<_> = self.emplacer.emplace(mapped); + mutable_ref.into() } }; - let __mapper = __InlineMapper { emplacer }; - ::map_with::(__mapper, self) + let __mapper = __InlineMapper { emplacer, span }; + let content = ::map_with::(__mapper, self); + ::covariant::(content) } } diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs index d466b492..c9086171 100644 --- a/src/expressions/concepts/forms/simple_ref.rs +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -26,16 +26,24 @@ impl IsForm for BeRef {} impl IsHierarchicalForm for BeRef { type Leaf<'a, T: IsLeafType> = &'a T::Leaf; + + #[inline] + fn covariant_leaf<'a, 'b, T: IsLeafType>(leaf: Self::Leaf<'a, T>) -> Self::Leaf<'b, T> + where + 'a: 'b, + { + leaf + } } impl IsDynCompatibleForm for BeRef { - type DynLeaf<'a, D: 'static + ?Sized> = &'a D; + type DynLeaf<'a, D: IsDynType> = &'a D::DynContent; - fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( - leaf: Self::Leaf<'a, T>, + fn leaf_to_dyn<'a, T: IsLeafType, D: IsDynType>( + Spanned(leaf, _span): Spanned>, ) -> Result, Content<'a, T, Self>> where - T::Leaf: CastDyn, + T::Leaf: CastDyn, { ::map_ref(leaf) } @@ -52,17 +60,19 @@ where Self: IsValueContent, Self::Type: IsHierarchicalType = Self>, { - fn into_shared<'b, T: 'static>( + fn into_shared<'o, 'b, T: 'static>( self, emplacer: &'b mut SharedEmplacer<'a, T>, - ) -> Content<'static, Self::Type, BeShared> + span: Option, + ) -> Content<'o, Self::Type, BeShared> where Self: Sized, { - struct __InlineMapper<'b, 'e2, X: 'static> { - emplacer: &'b mut SharedEmplacer<'e2, X>, + struct __InlineMapper<'b, 'e, X: 'static> { + emplacer: &'b mut SharedEmplacer<'e, X>, + span: Option, } - impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + impl<'b, 'e, X> LeafMapper for __InlineMapper<'b, 'e, X> { type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeShared>; fn to_parent_output<'a, T: IsChildType>( @@ -75,23 +85,34 @@ where self, leaf: ::Leaf<'l, T>, ) -> Self::Output<'l, T> { - // SAFETY: 'l = 'a = 'e so this is valid - unsafe { self.emplacer.emplace_unchecked(leaf) } + // SAFETY: 'l = 'a = 'e2 so the lifetime transmute is valid, and + // PathExtension::Tightened correctly describes type narrowing + let mapped = unsafe { + MappedRef::new_unchecked( + leaf, + PathExtension::Tightened(T::type_kind()), + self.span, + ) + }; + self.emplacer.emplace(mapped) } }; - let __mapper = __InlineMapper { emplacer }; - ::map_with::(__mapper, self) + let __mapper = __InlineMapper { emplacer, span }; + let content = ::map_with::(__mapper, self); + ::covariant::(content) } - fn into_shared_any_ref<'b, T: 'static>( + fn into_shared_any_ref<'o, 'b, T: 'static>( self, emplacer: &'b mut SharedEmplacer<'a, T>, - ) -> Content<'static, Self::Type, BeAnyRef> + span: Option, + ) -> Content<'o, Self::Type, BeAnyRef> where Self: Sized, { struct __InlineMapper<'b, 'e2, X: 'static> { emplacer: &'b mut SharedEmplacer<'e2, X>, + span: Option, } impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAnyRef>; @@ -106,12 +127,22 @@ where 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() } + // SAFETY: 'l = 'a = 'e2 so the lifetime transmute is valid, and + // PathExtension::Tightened correctly describes type narrowing + let mapped = unsafe { + MappedRef::new_unchecked( + leaf, + PathExtension::Tightened(T::type_kind()), + self.span, + ) + }; + let shared_ref: SharedReference<_> = self.emplacer.emplace(mapped); + shared_ref.into() } }; - let __mapper = __InlineMapper { emplacer }; - ::map_with::(__mapper, self) + let __mapper = __InlineMapper { emplacer, span }; + let content = ::map_with::(__mapper, self); + ::covariant::(content) } } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index d267a660..02b09942 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -42,6 +42,15 @@ pub(crate) trait IsHierarchicalType: IsType { fn content_to_leaf_kind( content: &Self::Content<'_, F>, ) -> Self::LeafKind; + + /// Shrinks the lifetime of a content value from `'a` to `'b`. + /// This is safe because all forms' leaf types are covariant in `'a`, + /// as enforced by [`IsHierarchicalForm::covariant_leaf`]. + fn covariant<'a, 'b, F: IsHierarchicalForm>( + content: Self::Content<'a, F>, + ) -> Self::Content<'b, F> + where + 'a: 'b; } pub(crate) trait IsLeafType: @@ -57,7 +66,6 @@ pub(crate) trait IsLeafType: + 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>> @@ -106,6 +114,7 @@ pub(crate) trait DowncastFrom: IsHierarchicalType { pub(crate) trait DynResolveFrom: IsDynType { fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( content: Content<'a, T, F>, + span: SpanRange, ) -> Result, Content<'a, T, F>>; fn resolve<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( @@ -113,7 +122,7 @@ pub(crate) trait DynResolveFrom: IsDynType { span_range: SpanRange, resolution_target: &str, ) -> FunctionResult> { - let content = match Self::downcast_from(content) { + let content = match Self::downcast_from(content, span_range) { Ok(c) => c, Err(existing) => { let leaf_kind = T::content_to_leaf_kind::(&existing); @@ -455,6 +464,20 @@ macro_rules! define_parent_type { ) -> Self::LeafKind { content.kind() } + + #[inline] + fn covariant<'a, 'b, F: IsHierarchicalForm>( + content: Self::Content<'a, F>, + ) -> Self::Content<'b, F> + where + 'a: 'b, + { + match content { + $( $content::$variant(x) => $content::$variant( + <$variant_type>::covariant::(x) + ), )* + } + } } $content_vis enum $content<'a, F: IsHierarchicalForm> { @@ -472,7 +495,6 @@ macro_rules! define_parent_type { } } - impl<'a, F: IsHierarchicalForm> Copy for $content<'a, F> where $( Content<'a, $variant_type, F>: Copy ),* @@ -634,6 +656,16 @@ macro_rules! define_leaf_type { ) -> Self::LeafKind { $kind } + + #[inline] + fn covariant<'a, 'b, F: IsHierarchicalForm>( + content: Self::Content<'a, F>, + ) -> Self::Content<'b, F> + where + 'a: 'b, + { + F::covariant_leaf::(content) + } } impl IsLeafType for $type_def { @@ -712,11 +744,17 @@ macro_rules! define_leaf_type { pub(crate) use define_leaf_type; -pub(crate) struct DynMapper(std::marker::PhantomData); +pub(crate) struct DynMapper { + _phantom: std::marker::PhantomData, + pub(crate) span: SpanRange, +} -impl DynMapper { - pub(crate) const fn new() -> Self { - Self(std::marker::PhantomData) +impl DynMapper { + pub(crate) fn new(span: SpanRange) -> Self { + Self { + _phantom: std::marker::PhantomData, + span, + } } } @@ -770,7 +808,7 @@ pub(crate) use impl_value_content_traits; macro_rules! define_dyn_type { ( $type_def_vis:vis $type_def:ident, - content: $dyn_type:ty, + content: $dyn_content:ty, dyn_kind: DynTypeKind::$dyn_kind:ident, type_name: $source_type_name:literal, articled_value_name: $articled_value_name:literal, @@ -802,53 +840,53 @@ macro_rules! define_dyn_type { } impl IsDynType for $type_def { - type DynContent = $dyn_type; + type DynContent = $dyn_content; } impl_type_feature_resolver! { impl TypeFeatureResolver for $type_def: [$type_def] } - impl IsDynLeaf for $dyn_type { + impl IsDynLeaf for $dyn_content { type Type = $type_def; } - impl IsArgument for Box<$dyn_type> { + impl IsArgument for Box<$dyn_content> { type ValueType = $type_def; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; fn from_argument(Spanned(value, span_range): Spanned) -> FunctionResult { - let form_mapped = BeOwned::from_argument_value(value)?; + let form_mapped = BeOwned::from_argument_value(Spanned(value, span_range))?; <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") } } - impl<'a> IsArgument for AnyRef<'a, $dyn_type> { + impl<'a> IsArgument for AnyRef<'a, $dyn_content> { type ValueType = $type_def; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; fn from_argument(Spanned(value, span_range): Spanned) -> FunctionResult { - let form_mapped = BeAnyRef::from_argument_value(value)?; + let form_mapped = BeAnyRef::from_argument_value(Spanned(value, span_range))?; <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") } } - impl<'a> IsArgument for AnyMut<'a, $dyn_type> { + impl<'a> IsArgument for AnyMut<'a, $dyn_content> { type ValueType = $type_def; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; fn from_argument(Spanned(value, span_range): Spanned) -> FunctionResult { - let form_mapped = BeAnyMut::from_argument_value(value)?; + let form_mapped = BeAnyMut::from_argument_value(Spanned(value, span_range))?; <$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>) -> Result, Content<'a, T, F>> { - T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content) + fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>(content: Content<'a, T, F>, span: SpanRange) -> Result, Content<'a, T, F>> { + T::map_with::<'a, F, _>(DynMapper::<$type_def>::new(span), content) } } - impl LeafMapper for DynMapper<$dyn_type> { - type Output<'a, T: IsHierarchicalType> = Result, Content<'a, T, F>>; + impl LeafMapper for DynMapper<$type_def> { + type Output<'a, T: IsHierarchicalType> = Result, Content<'a, T, F>>; fn to_parent_output<'a, T: IsChildType>( output: Self::Output<'a, T>, @@ -863,7 +901,7 @@ macro_rules! define_dyn_type { self, leaf: F::Leaf<'a, T>, ) -> Self::Output<'a, T> { - F::leaf_to_dyn(leaf) + F::leaf_to_dyn(Spanned(leaf, self.span)) } } }; diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index f32972a8..d88224a3 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -101,14 +101,14 @@ impl DisabledArgumentValue { DisabledArgumentValue::CopyOnWrite(copy_on_write) => { Ok(ArgumentValue::CopyOnWrite(copy_on_write.enable(span)?)) } - DisabledArgumentValue::Mutable(mutable) => { - Ok(ArgumentValue::Mutable(mutable.enable(span)?)) + DisabledArgumentValue::Mutable(inactive) => { + Ok(ArgumentValue::Mutable(inactive.activate(span)?)) } - DisabledArgumentValue::Assignee(assignee) => { - Ok(ArgumentValue::Assignee(Assignee(assignee.enable(span)?))) + DisabledArgumentValue::Assignee(inactive) => { + Ok(ArgumentValue::Assignee(Assignee(inactive.activate(span)?))) } - DisabledArgumentValue::Shared(shared) => { - Ok(ArgumentValue::Shared(shared.enable(span)?)) + DisabledArgumentValue::Shared(inactive) => { + Ok(ArgumentValue::Shared(inactive.activate(span)?)) } } } @@ -452,13 +452,15 @@ impl ArgumentOwnership { ArgumentOwnership::Owned => Ok(ArgumentValue::Owned( copy_on_write.clone_to_owned_transparently(span)?, )), - ArgumentOwnership::Shared => Ok(ArgumentValue::Shared(copy_on_write.into_shared())), + ArgumentOwnership::Shared => Ok(ArgumentValue::Shared(copy_on_write.into_shared(span))), ArgumentOwnership::Mutable => { if copy_on_write.acts_as_shared_reference() { 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.clone_to_owned_transparently(span)?, + None, + span, ))) } } @@ -559,9 +561,9 @@ impl ArgumentOwnership { ArgumentOwnership::CopyOnWrite => { Ok(ArgumentValue::CopyOnWrite(CopyOnWrite::owned(owned))) } - ArgumentOwnership::Mutable => { - Ok(ArgumentValue::Mutable(Mutable::new_from_owned(owned))) - } + ArgumentOwnership::Mutable => Ok(ArgumentValue::Mutable(Mutable::new_from_owned( + owned, None, span, + ))), ArgumentOwnership::Assignee { .. } => { if is_from_last_use { span.ownership_err("The final usage of a variable cannot be assigned to. You can use `let _ = ..` to discard a value.") @@ -569,7 +571,9 @@ impl ArgumentOwnership { span.ownership_err("An owned value cannot be assigned to.") } } - ArgumentOwnership::Shared => Ok(ArgumentValue::Shared(Shared::new_from_owned(owned))), + ArgumentOwnership::Shared => Ok(ArgumentValue::Shared(Shared::new_from_owned( + owned, None, span, + ))), } } } @@ -1050,6 +1054,7 @@ impl EvaluationFrame for ValuePropertyAccessBuilder { let ctx = PropertyAccessCallContext { property: &self.access, + output_span_range: result_span, }; let auto_create = context.requested_ownership().requests_auto_create(); @@ -1145,8 +1150,10 @@ impl EvaluationFrame for ValueIndexAccessBuilder { let index = value.expect_shared(); let index = index.as_ref_value().spanned(span); + let result_span = SpanRange::new_between(source_span, self.access.span_range()); let ctx = IndexAccessCallContext { access: &self.access, + output_span_range: result_span, }; let auto_create = context.requested_ownership().requests_auto_create(); @@ -1163,7 +1170,6 @@ impl EvaluationFrame for ValueIndexAccessBuilder { }, |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/expression_parsing.rs b/src/expressions/expression_parsing.rs index 7784557c..4bc53b20 100644 --- a/src/expressions/expression_parsing.rs +++ b/src/expressions/expression_parsing.rs @@ -123,7 +123,7 @@ impl<'a> ExpressionParser<'a> { "true" | "false" => { let bool = input.parse::()?; UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(bool.value.into_any_value()), + SharedValue::new_from_owned(bool.value.into_any_value(), None, bool.span.span_range()), bool.span.span_range(), ))) } @@ -136,7 +136,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_any_value()), + SharedValue::new_from_owned(().into_any_value(), None, none_ident.span().span_range()), none_ident.span().span_range(), ))) } @@ -155,7 +155,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(AnyValue::for_syn_lit(lit)), + SharedValue::new_from_owned(AnyValue::for_syn_lit(lit), None, span_range), span_range, ))) }, diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index 57f97a28..be59dc96 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -58,12 +58,13 @@ impl IsArgument for ArgumentValue { } } -impl + ResolvableArgumentTarget + ?Sized> IsArgument for Shared { - type ValueType = T::ValueType; +impl IsArgument for Shared { + type ValueType = AnyType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; fn from_argument(argument: Spanned) -> FunctionResult { - T::resolve_shared(argument.expect_shared(), "This argument") + let Spanned(shared, _span) = argument.expect_shared(); + Ok(shared) } } @@ -90,12 +91,13 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgum } } -impl + ResolvableArgumentTarget + ?Sized> IsArgument for Mutable { - type ValueType = T::ValueType; +impl IsArgument for Mutable { + type ValueType = AnyType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; fn from_argument(argument: Spanned) -> FunctionResult { - T::resolve_mutable(argument.expect_mutable(), "This argument") + let Spanned(mutable, _span) = argument.expect_mutable(); + Ok(mutable) } } @@ -165,7 +167,9 @@ impl> ResolveAs for Spanned { } } -impl + ?Sized> ResolveAs> for Spanned { +impl + ?Sized + 'static> ResolveAs> + for Spanned +{ fn resolve_as(self, resolution_target: &str) -> FunctionResult> { T::resolve_shared(self, resolution_target) } @@ -185,7 +189,9 @@ impl<'a, T: ResolvableShared + ?Sized> ResolveAs> } } -impl + ?Sized> ResolveAs> for Spanned { +impl + ?Sized + 'static> ResolveAs> + for Spanned +{ fn resolve_as(self, resolution_target: &str) -> FunctionResult> { T::resolve_mutable(self, resolution_target) } @@ -211,7 +217,7 @@ pub(crate) trait ResolvableArgumentTarget { type ValueType: TypeData; } -pub(crate) trait ResolvableOwned: Sized { +pub(crate) trait ResolvableOwned: Sized { fn resolve_from_value(value: T, context: ResolutionContext) -> FunctionResult; fn resolve_spanned_from_value( @@ -235,23 +241,34 @@ pub(crate) trait ResolvableOwned: Sized { } } -pub(crate) trait ResolvableShared { +pub(crate) trait ResolvableShared { fn resolve_from_ref<'a>(value: &'a T, context: ResolutionContext) -> FunctionResult<&'a Self>; /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" fn resolve_shared( Spanned(value, span): Spanned>, resolution_target: &str, - ) -> FunctionResult> { + ) -> FunctionResult> + where + Self: 'static, + { value .try_map(|v| { - Self::resolve_from_ref( + let resolved = Self::resolve_from_ref( v, ResolutionContext { span_range: &span, resolution_target, }, - ) + )?; + // SAFETY: Tightened(T::Type) correctly describes type narrowing + Ok(unsafe { + MappedRef::new( + resolved, + PathExtension::Tightened(T::Type::type_kind()), + span, + ) + }) }) .map_err(|(err, _)| err) } @@ -285,7 +302,7 @@ pub(crate) trait ResolvableShared { } } -pub(crate) trait ResolvableMutable { +pub(crate) trait ResolvableMutable { fn resolve_from_mut<'a>( value: &'a mut T, context: ResolutionContext, @@ -304,16 +321,27 @@ pub(crate) trait ResolvableMutable { fn resolve_mutable( Spanned(value, span): Spanned>, resolution_target: &str, - ) -> FunctionResult> { + ) -> FunctionResult> + where + Self: 'static, + { value .try_map(|v| { - Self::resolve_from_mut( + let resolved = Self::resolve_from_mut( v, ResolutionContext { span_range: &span, resolution_target, }, - ) + )?; + // SAFETY: Tightened(T::Type) correctly describes type narrowing + Ok(unsafe { + MappedMut::new( + resolved, + PathExtension::Tightened(T::Type::type_kind()), + span, + ) + }) }) .map_err(|(err, _)| err) } @@ -409,46 +437,3 @@ macro_rules! impl_resolvable_argument_for { } pub(crate) use impl_resolvable_argument_for; - -macro_rules! impl_delegated_resolvable_argument_for { - (($value:ident: $delegate:ty) -> $type:ty { $expr:expr }) => { - impl ResolvableArgumentTarget for $type { - type ValueType = <$delegate as ResolvableArgumentTarget>::ValueType; - } - - impl ResolvableOwned for $type { - fn resolve_from_value( - input_value: Value, - context: ResolutionContext, - ) -> FunctionResult { - let $value: $delegate = - ResolvableOwned::::resolve_from_value(input_value, context)?; - Ok($expr) - } - } - - impl ResolvableShared for $type { - fn resolve_from_ref<'a>( - input_value: &'a Value, - context: ResolutionContext, - ) -> FunctionResult<&'a Self> { - let $value: &$delegate = - ResolvableShared::::resolve_from_ref(input_value, context)?; - Ok(&$expr) - } - } - - impl ResolvableMutable for $type { - fn resolve_from_mut<'a>( - input_value: &'a mut Value, - context: ResolutionContext, - ) -> FunctionResult<&'a mut Self> { - let $value: &mut $delegate = - ResolvableMutable::::resolve_from_mut(input_value, context)?; - Ok(&mut $expr) - } - } - }; -} - -pub(crate) use impl_delegated_resolvable_argument_for; diff --git a/src/expressions/type_resolution/interface_macros.rs b/src/expressions/type_resolution/interface_macros.rs index 245b0bb0..f8b1c46f 100644 --- a/src/expressions/type_resolution/interface_macros.rs +++ b/src/expressions/type_resolution/interface_macros.rs @@ -313,10 +313,10 @@ where // ============================================================================ pub(crate) fn apply_property_shared<'a, S: ResolvableShared + ?Sized + 'a>( - f: for<'b> fn(PropertyAccessCallContext, &'b S) -> FunctionResult<&'b AnyValue>, + f: for<'b> fn(PropertyAccessCallContext, &'b S) -> FunctionResult>, ctx: PropertyAccessCallContext, source: &'a AnyValue, -) -> FunctionResult<&'a AnyValue> { +) -> FunctionResult> { let source = S::resolve_from_ref( source, ResolutionContext::new(&ctx.property.span_range(), "The property access source"), @@ -325,11 +325,15 @@ pub(crate) fn apply_property_shared<'a, S: ResolvableShared + ?Sized + } pub(crate) fn apply_property_mutable<'a, S: ResolvableMutable + ?Sized + 'a>( - f: for<'b> fn(PropertyAccessCallContext, &'b mut S, bool) -> FunctionResult<&'b mut AnyValue>, + f: for<'b> fn( + PropertyAccessCallContext, + &'b mut S, + bool, + ) -> FunctionResult>, ctx: PropertyAccessCallContext, source: &'a mut AnyValue, auto_create: bool, -) -> FunctionResult<&'a mut AnyValue> { +) -> FunctionResult> { let source = S::resolve_from_mut( source, ResolutionContext::new(&ctx.property.span_range(), "The property access source"), @@ -358,11 +362,11 @@ pub(crate) fn apply_index_shared<'a, S: ResolvableShared + ?Sized + 'a IndexAccessCallContext, &'b S, Spanned, - ) -> FunctionResult<&'b AnyValue>, + ) -> FunctionResult>, ctx: IndexAccessCallContext, source: &'a AnyValue, index: Spanned, -) -> FunctionResult<&'a AnyValue> { +) -> FunctionResult> { let source = S::resolve_from_ref( source, ResolutionContext::new(&ctx.access.span_range(), "The index access source"), @@ -370,18 +374,19 @@ pub(crate) fn apply_index_shared<'a, S: ResolvableShared + ?Sized + 'a f(ctx, source, index) } +#[allow(clippy::type_complexity)] pub(crate) fn apply_index_mutable<'a, S: ResolvableMutable + ?Sized + 'a>( f: for<'b> fn( IndexAccessCallContext, &'b mut S, Spanned, bool, - ) -> FunctionResult<&'b mut AnyValue>, + ) -> FunctionResult>, ctx: IndexAccessCallContext, source: &'a mut AnyValue, index: Spanned, auto_create: bool, -) -> FunctionResult<&'a mut AnyValue> { +) -> FunctionResult> { let source = S::resolve_from_mut( source, ResolutionContext::new(&ctx.access.span_range(), "The index access source"), @@ -601,9 +606,9 @@ macro_rules! define_type_features { #[allow(unused)] use super::*; - pub(crate) fn shared<'a>(if_empty!([$($property_shared_context)?][_ctx]): PropertyAccessCallContext, $($property_shared_args)*) -> FunctionResult<&'a AnyValue> $property_shared_body + pub(crate) fn shared<'a>(if_empty!([$($property_shared_context)?][_ctx]): PropertyAccessCallContext, $($property_shared_args)*) -> FunctionResult> $property_shared_body - pub(crate) fn mutable<'a>(if_empty!([$($property_mutable_context)?][_ctx]): PropertyAccessCallContext, $($property_mutable_args)*) -> FunctionResult<&'a mut AnyValue> $property_mutable_body + pub(crate) fn mutable<'a>(if_empty!([$($property_mutable_context)?][_ctx]): PropertyAccessCallContext, $($property_mutable_args)*) -> FunctionResult> $property_mutable_body pub(crate) fn owned(if_empty!([$($property_owned_context)?][_ctx]): PropertyAccessCallContext, $($property_owned_args)*) -> FunctionResult $property_owned_body } @@ -622,9 +627,9 @@ macro_rules! define_type_features { #[allow(unused)] use super::*; - pub(crate) fn shared<'a>(if_empty!([$($index_shared_context)?][_ctx]): IndexAccessCallContext, $($index_shared_args)*) -> FunctionResult<&'a AnyValue> $index_shared_body + pub(crate) fn shared<'a>(if_empty!([$($index_shared_context)?][_ctx]): IndexAccessCallContext, $($index_shared_args)*) -> FunctionResult> $index_shared_body - pub(crate) fn mutable<'a>(if_empty!([$($index_mutable_context)?][_ctx]): IndexAccessCallContext, $($index_mutable_args)*) -> FunctionResult<&'a mut AnyValue> $index_mutable_body + pub(crate) fn mutable<'a>(if_empty!([$($index_mutable_context)?][_ctx]): IndexAccessCallContext, $($index_mutable_args)*) -> FunctionResult> $index_mutable_body pub(crate) fn owned(if_empty!([$($index_owned_context)?][_ctx]): IndexAccessCallContext, $($index_owned_args)*) -> FunctionResult $index_owned_body } diff --git a/src/expressions/type_resolution/type_data.rs b/src/expressions/type_resolution/type_data.rs index c31cd275..c678c584 100644 --- a/src/expressions/type_resolution/type_data.rs +++ b/src/expressions/type_resolution/type_data.rs @@ -338,23 +338,28 @@ impl BinaryOperationInterface { #[derive(Clone, Copy)] pub(crate) struct PropertyAccessCallContext<'a> { pub property: &'a PropertyAccess, + pub output_span_range: SpanRange, } /// 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. +/// returns references into the source value. The shared and mutable access methods +/// return [`MappedRef`]/[`MappedMut`] which bundle the result with a [`PathExtension`] +/// describing the navigation. pub(crate) struct PropertyAccessInterface { - /// Access a property by shared reference. - pub shared_access: - for<'a> fn(PropertyAccessCallContext, &'a AnyValue) -> FunctionResult<&'a AnyValue>, + /// Access a property by shared reference, returning a [`MappedRef`] with path info. + pub shared_access: for<'a> fn( + PropertyAccessCallContext, + &'a AnyValue, + ) -> FunctionResult>, /// Access a property by mutable reference, optionally auto-creating if missing. + /// Returns a [`MappedMut`] with path info. pub mutable_access: for<'a> fn( PropertyAccessCallContext, &'a mut AnyValue, bool, - ) -> FunctionResult<&'a mut AnyValue>, + ) -> FunctionResult>, /// Extract a property from an owned value. pub owned_access: fn(PropertyAccessCallContext, AnyValue) -> FunctionResult, } @@ -367,28 +372,31 @@ pub(crate) struct PropertyAccessInterface { #[derive(Clone, Copy)] pub(crate) struct IndexAccessCallContext<'a> { pub access: &'a IndexAccess, + pub output_span_range: SpanRange, } /// 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. +/// rather than a static identifier. The shared and mutable access methods +/// return [`MappedRef`]/[`MappedMut`] which bundle the result with a [`PathExtension`]. pub(crate) struct IndexAccessInterface { /// The ownership requirement for the index value. pub index_ownership: ArgumentOwnership, - /// Access an element by shared reference. + /// Access an element by shared reference, returning a [`MappedRef`] with path info. pub shared_access: for<'a> fn( IndexAccessCallContext, &'a AnyValue, Spanned, - ) -> FunctionResult<&'a AnyValue>, + ) -> FunctionResult>, /// Access an element by mutable reference, optionally auto-creating if missing. + /// Returns a [`MappedMut`] with path info. pub mutable_access: for<'a> fn( IndexAccessCallContext, &'a mut AnyValue, Spanned, bool, - ) -> FunctionResult<&'a mut AnyValue>, + ) -> FunctionResult>, /// Extract an element from an owned value. pub owned_access: fn(IndexAccessCallContext, AnyValue, Spanned) -> FunctionResult, diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs index c089e77e..7fe69984 100644 --- a/src/expressions/type_resolution/type_kinds.rs +++ b/src/expressions/type_resolution/type_kinds.rs @@ -94,8 +94,10 @@ impl TypeKind { pub(crate) fn is_tightening_to(&self, other: &Self) -> bool { match self.compare_bindings(other) { TypeBindingComparison::Equal => true, - TypeBindingComparison::RightDerivesFromLeft => true, - TypeBindingComparison::LeftDerivesFromRight => false, + TypeBindingComparison::RightDerivesFromLeftButIsNotSubtype => true, + TypeBindingComparison::RightIsSubtypeOfLeft => true, + TypeBindingComparison::LeftDerivesFromRightButIsNotSubtype => false, + TypeBindingComparison::LeftIsSubtypeOfRight => false, TypeBindingComparison::Incomparable => false, TypeBindingComparison::Incompatible => false, } @@ -117,24 +119,24 @@ impl TypeKind { (TypeKind::Dyn(_), TypeKind::Dyn(_)) => TypeBindingComparison::Incomparable, // Assuming the values are compatible, a dyn can be derived from any leaf/parent, // but not the other way around (at present at least) - (TypeKind::Dyn(_), _) => TypeBindingComparison::LeftDerivesFromRight, - (_, TypeKind::Dyn(_)) => TypeBindingComparison::RightDerivesFromLeft, + (TypeKind::Dyn(_), _) => TypeBindingComparison::LeftDerivesFromRightButIsNotSubtype, + (_, TypeKind::Dyn(_)) => TypeBindingComparison::RightDerivesFromLeftButIsNotSubtype, // All non-any-values are strict subtypes of AnyValue (TypeKind::Parent(ParentTypeKind::Value(_)), _) => { - TypeBindingComparison::RightDerivesFromLeft + TypeBindingComparison::RightIsSubtypeOfLeft } (_, TypeKind::Parent(ParentTypeKind::Value(_))) => { - TypeBindingComparison::LeftDerivesFromRight + TypeBindingComparison::LeftIsSubtypeOfRight } // Integer types ( TypeKind::Parent(ParentTypeKind::Integer(_)), TypeKind::Leaf(AnyValueLeafKind::Integer(_)), - ) => TypeBindingComparison::RightDerivesFromLeft, + ) => TypeBindingComparison::RightIsSubtypeOfLeft, ( TypeKind::Leaf(AnyValueLeafKind::Integer(_)), TypeKind::Parent(ParentTypeKind::Integer(_)), - ) => TypeBindingComparison::LeftDerivesFromRight, + ) => TypeBindingComparison::LeftIsSubtypeOfRight, (TypeKind::Parent(ParentTypeKind::Integer(_)), _) => { TypeBindingComparison::Incompatible } @@ -145,11 +147,11 @@ impl TypeKind { ( TypeKind::Parent(ParentTypeKind::Float(_)), TypeKind::Leaf(AnyValueLeafKind::Float(_)), - ) => TypeBindingComparison::RightDerivesFromLeft, + ) => TypeBindingComparison::RightIsSubtypeOfLeft, ( TypeKind::Leaf(AnyValueLeafKind::Float(_)), TypeKind::Parent(ParentTypeKind::Float(_)), - ) => TypeBindingComparison::LeftDerivesFromRight, + ) => TypeBindingComparison::LeftIsSubtypeOfRight, (TypeKind::Parent(ParentTypeKind::Float(_)), _) => TypeBindingComparison::Incompatible, (_, TypeKind::Parent(ParentTypeKind::Float(_))) => TypeBindingComparison::Incompatible, // Non-equal leaf types are incomparable @@ -163,8 +165,14 @@ impl TypeKind { // is compatible with some other form. pub(crate) enum TypeBindingComparison { Equal, - RightDerivesFromLeft, - LeftDerivesFromRight, + // e.g. U8Value is a subtype of IntegerValue + RightIsSubtypeOfLeft, + // e.g. dyn IterableValue derives from ArrayValue, but isn't a subtype of it. + RightDerivesFromLeftButIsNotSubtype, + // e.g. U8Value is a subtype of IntegerValue + LeftIsSubtypeOfRight, + // e.g. dyn IterableValue derives from ArrayValue, but isn't a subtype of it. + LeftDerivesFromRightButIsNotSubtype, Incomparable, // Indicates that the types are incompatible. // Such a comparison shouldn't arise between valid references. @@ -322,22 +330,34 @@ impl TypeProperty { ) -> FunctionResult> { let resolver = self.source_type.kind.feature_resolver(); // TODO[performance] - lazily initialize properties as Shared - let property_name = &self.property.to_string(); - if let Some(value) = resolver.resolve_type_property(property_name) { + let property_name = self.property.to_string(); + if let Some(value) = resolver.resolve_type_property(&property_name) { return ownership.map_from_shared(Spanned( - SharedValue::new_from_owned(value.into_any_value()), + SharedValue::new_from_owned( + value.into_any_value(), + Some(property_name), + self.span_range(), + ), self.span_range(), )); } - if let Some(method) = resolver.resolve_method(property_name) { + if let Some(method) = resolver.resolve_method(&property_name) { return ownership.map_from_shared(Spanned( - SharedValue::new_from_owned(method.into_any_value()), + SharedValue::new_from_owned( + method.into_any_value(), + Some(property_name.clone()), + self.span_range(), + ), self.span_range(), )); } - if let Some(function) = resolver.resolve_type_function(property_name) { + if let Some(function) = resolver.resolve_type_function(&property_name) { return ownership.map_from_shared(Spanned( - SharedValue::new_from_owned(function.into_any_value()), + SharedValue::new_from_owned( + function.into_any_value(), + Some(property_name.clone()), + self.span_range(), + ), self.span_range(), )); } diff --git a/src/expressions/values/any_value.rs b/src/expressions/values/any_value.rs index d3799c35..ceea4b08 100644 --- a/src/expressions/values/any_value.rs +++ b/src/expressions/values/any_value.rs @@ -50,7 +50,7 @@ define_type_features! { fn as_mut(Spanned(this, span): Spanned) -> FunctionResult { Ok(match this { - ArgumentValue::Owned(owned) => Mutable::new_from_owned(owned), + ArgumentValue::Owned(owned) => Mutable::new_from_owned(owned, None, span), ArgumentValue::CopyOnWrite(copy_on_write) => ArgumentOwnership::Mutable .map_from_copy_on_write(Spanned(copy_on_write, span))? .expect_mutable(), diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index daf4b5ea..d40ade4c 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -59,42 +59,6 @@ impl ArrayValue { }) } - pub(super) fn index_mut( - &mut self, - Spanned(index, span_range): Spanned, - ) -> FunctionResult<&mut AnyValue> { - Ok(match index { - AnyValueContent::Integer(integer) => { - let index = - self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; - &mut self.items[index] - } - AnyValueContent::Range(..) => { - // 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"), - }) - } - - pub(super) fn index_ref( - &self, - Spanned(index, span_range): Spanned, - ) -> FunctionResult<&AnyValue> { - Ok(match index { - AnyValueContent::Integer(integer) => { - let index = - self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; - &self.items[index] - } - AnyValueContent::Range(..) => { - // 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"), - }) - } - pub(super) fn resolve_valid_index( &self, Spanned(index, span_range): Spanned, @@ -233,11 +197,51 @@ define_type_features! { } } index_access(ArrayValue) { - fn shared(source: &'a ArrayValue, index: Spanned) { - source.index_ref(index) + [ctx] fn shared(source: &'a ArrayValue, Spanned(index, span_range): Spanned) { + match index { + AnyValueContent::Integer(integer) => { + let idx = source.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; + // SAFETY: ArrayChild correctly describes navigating to an array element + Ok(unsafe { + MappedRef::new( + &source.items[idx], + PathExtension::Child( + ChildSpecifier::ArrayChild(idx), + AnyType::type_kind(), + ), + ctx.output_span_range, + ) + }) + } + AnyValueContent::Range(..) => { + // TODO[slice-support] Temporary until we add slice types - we error here + span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]") + } + _ => span_range.type_err("The index must be an integer or a range"), + } } - fn mutable(source: &'a mut ArrayValue, index: Spanned, _auto_create: bool) { - source.index_mut(index) + [ctx] fn mutable(source: &'a mut ArrayValue, Spanned(index, span_range): Spanned, _auto_create: bool) { + match index { + AnyValueContent::Integer(integer) => { + let idx = source.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; + // SAFETY: ArrayChild correctly describes navigating to an array element + Ok(unsafe { + MappedMut::new( + &mut source.items[idx], + PathExtension::Child( + ChildSpecifier::ArrayChild(idx), + AnyType::type_kind(), + ), + ctx.output_span_range, + ) + }) + } + AnyValueContent::Range(..) => { + // TODO[slice-support] Temporary until we add slice types - we error here + span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]") + } + _ => span_range.type_err("The index must be an integer or a range"), + } } fn owned(source: ArrayValue, index: Spanned) { source.into_indexed(index) diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index aa8dd59b..e927826f 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -72,23 +72,6 @@ impl ObjectValue { } } - pub(super) fn index_mut( - &mut self, - index: Spanned, - auto_create: bool, - ) -> FunctionResult<&mut AnyValue> { - 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) -> FunctionResult<&AnyValue> { - let key: Spanned<&str> = index.downcast_resolve("An object key")?; - match self.entries.get(*key) { - Some(entry) => Ok(&entry.value), - None => Ok(static_none_ref()), - } - } - pub(super) fn property_mut( &mut self, access: &PropertyAccess, @@ -286,21 +269,72 @@ define_type_features! { } property_access(ObjectValue) { [ctx] fn shared(source: &'a ObjectValue) { - source.property_ref(ctx.property) + let value = source.property_ref(ctx.property)?; + // SAFETY: ObjectChild correctly describes navigating to a named property + Ok(unsafe { + MappedRef::new( + value, + PathExtension::Child( + ChildSpecifier::ObjectChild(ctx.property.property.to_string()), + AnyType::type_kind(), + ), + ctx.output_span_range, + ) + }) } [ctx] fn mutable(source: &'a mut ObjectValue, auto_create: bool) { - source.property_mut(ctx.property, auto_create) + let value = source.property_mut(ctx.property, auto_create)?; + // SAFETY: ObjectChild correctly describes navigating to a named property + Ok(unsafe { + MappedMut::new( + value, + PathExtension::Child( + ChildSpecifier::ObjectChild(ctx.property.property.to_string()), + AnyType::type_kind(), + ), + ctx.output_span_range, + ) + }) } [ctx] fn owned(source: ObjectValue) { source.into_property(ctx.property) } } index_access(ObjectValue) { - fn shared(source: &'a ObjectValue, index: Spanned) { - source.index_ref(index) + [ctx] fn shared(source: &'a ObjectValue, index: Spanned) { + let key: Spanned<&str> = index.downcast_resolve("An object key")?; + let key_string = key.to_string(); + let value = match source.entries.get(*key) { + Some(entry) => &entry.value, + None => static_none_ref(), + }; + // SAFETY: ObjectChild correctly describes navigating to a named key + Ok(unsafe { + MappedRef::new( + value, + PathExtension::Child( + ChildSpecifier::ObjectChild(key_string), + AnyType::type_kind(), + ), + ctx.output_span_range, + ) + }) } - fn mutable(source: &'a mut ObjectValue, index: Spanned, auto_create: bool) { - source.index_mut(index, auto_create) + [ctx] fn mutable(source: &'a mut ObjectValue, index: Spanned, auto_create: bool) { + let key: Spanned<&str> = index.downcast_resolve("An object key")?; + let key_string = key.to_string(); + let value = source.mut_entry(key.map(|s| s.to_string()), auto_create)?; + // SAFETY: ObjectChild correctly describes navigating to a named key + Ok(unsafe { + MappedMut::new( + value, + PathExtension::Child( + ChildSpecifier::ObjectChild(key_string), + AnyType::type_kind(), + ), + ctx.output_span_range, + ) + }) } fn owned(source: ObjectValue, index: Spanned) { source.into_indexed(index) diff --git a/src/internal_prelude.rs b/src/internal_prelude.rs index 7fb94530..93d60f59 100644 --- a/src/internal_prelude.rs +++ b/src/internal_prelude.rs @@ -2,7 +2,7 @@ pub(crate) use proc_macro2::{extra::*, *}; pub(crate) use quote::ToTokens; pub(crate) use std::{ borrow::{Borrow, Cow}, - cell::{Ref, RefCell, RefMut}, + cell::{RefCell, RefMut}, collections::{BTreeMap, HashMap, HashSet}, fmt::Debug, fmt::Write, diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index c83c0355..fca3309b 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -1,7 +1,6 @@ use super::*; use std::borrow::{Borrow, ToOwned}; -use std::rc::Rc; pub(super) enum VariableState { Uninitialized, @@ -16,7 +15,7 @@ pub(super) enum VariableState { #[derive(Clone)] pub(crate) enum VariableContent { // Owned, but possibly with pre-existing references - Referenceable(Referenceable), + Referenceable(Referenceable), Shared(DisabledShared), Mutable(DisabledMutable), } @@ -24,8 +23,8 @@ pub(crate) enum VariableContent { impl VariableContent { fn into_owned_as_only_owner(self) -> Result, VariableContent> { match self { - VariableContent::Referenceable(data) => match Rc::try_unwrap(data) { - Ok(unique) => Ok(unique.into_inner()), + VariableContent::Referenceable(data) => match data.try_into_inner() { + Ok(owned) => Ok(owned), Err(original) => Err(VariableContent::Referenceable(original)), }, other => Err(other), @@ -181,15 +180,15 @@ impl VariableBinding { } fn into_mut(self) -> FunctionResult { + let span = self.variable_span.span_range(); match self.content { VariableContent::Referenceable(referenceable) => { - let inner = MutableSubRcRefCell::new(referenceable).map_err(|_| { + referenceable.new_active_mutable(span).map_err(|_| { self.variable_span .ownership_error::(MUTABLE_ERROR_MESSAGE) - })?; - Ok(Mutable(inner)) + }) } - VariableContent::Mutable(disabled) => disabled.enable(self.variable_span.span_range()), + VariableContent::Mutable(inactive) => inactive.activate(span), VariableContent::Shared(shared) => self .variable_span .ownership_err(SHARED_TO_MUTABLE_ERROR_MESSAGE), @@ -197,44 +196,44 @@ impl VariableBinding { } fn into_shared(self) -> FunctionResult { + let span = self.variable_span.span_range(); match self.content { VariableContent::Referenceable(referenceable) => { - let inner = SharedSubRcRefCell::new(referenceable).map_err(|_| { + referenceable.new_active_shared(span).map_err(|_| { self.variable_span .ownership_error::(SHARED_ERROR_MESSAGE) - })?; - Ok(Shared(inner)) + }) } - VariableContent::Mutable(mutable) => mutable - .into_shared() - .enable(self.variable_span.span_range()), - VariableContent::Shared(shared) => shared.enable(self.variable_span.span_range()), + VariableContent::Mutable(inactive) => inactive.into_shared().activate(span), + VariableContent::Shared(inactive) => inactive.activate(span), } } fn into_late_bound(self) -> FunctionResult { + let span = self.variable_span.span_range(); match self.content { VariableContent::Referenceable(referenceable) => { - match MutableSubRcRefCell::new(referenceable) { - Ok(mutable) => Ok(LateBoundValue::Mutable(Mutable(mutable))), - Err(referenceable) => { - let shared = SharedSubRcRefCell::new(referenceable).map_err(|_| { + match referenceable.new_active_mutable(span) { + Ok(mutable) => Ok(LateBoundValue::Mutable(mutable)), + Err(_) => { + // Mutable failed, try shared + let shared = referenceable.new_active_shared(span).map_err(|_| { self.variable_span .ownership_error::(SHARED_ERROR_MESSAGE) })?; Ok(LateBoundValue::Shared(LateBoundSharedValue::new( - Shared(shared), + shared, self.variable_span.syn_error(SHARED_ERROR_MESSAGE), ))) } } } - VariableContent::Mutable(mutable) => Ok(LateBoundValue::Mutable( - mutable.enable(self.variable_span.span_range())?, - )), - VariableContent::Shared(shared) => { + VariableContent::Mutable(inactive) => { + Ok(LateBoundValue::Mutable(inactive.activate(span)?)) + } + VariableContent::Shared(inactive) => { Ok(LateBoundValue::Shared(LateBoundSharedValue::new( - shared.enable(self.variable_span.span_range())?, + inactive.activate(span)?, self.variable_span .syn_error(SHARED_TO_MUTABLE_ERROR_MESSAGE), ))) @@ -353,17 +352,34 @@ impl Deref for LateBoundValue { } } +// ============================================================================ +// Type aliases: Old names → New dynamic reference types +// ============================================================================ + +/// Type alias: `Shared` is now `SharedReference`. +pub(crate) type Shared = SharedReference; + +/// Type alias: `Mutable` is now `MutableReference`. +pub(crate) type Mutable = MutableReference; + +/// Type alias: `DisabledShared` is now `InactiveSharedReference`. +pub(crate) type DisabledShared = InactiveSharedReference; + +/// Type alias: `DisabledMutable` is now `InactiveMutableReference`. +pub(crate) type DisabledMutable = InactiveMutableReference; + pub(crate) type AssigneeValue = AnyValueAssignee; +pub(crate) type SharedValue = AnyValueShared; /// A binding of a unique (mutable) reference to a value. /// See [`ArgumentOwnership::Assignee`] for more details. /// /// If you need span information, wrap with `Spanned>`. -pub(crate) struct Assignee(pub Mutable); +pub(crate) struct Assignee(pub MutableReference); impl AssigneeValue { pub(crate) fn set(&mut self, content: impl IntoAnyValue) { - *self.0 .0 = content.into_any_value(); + *self.0 = content.into_any_value(); } } @@ -380,8 +396,7 @@ where { fn into_content(self) -> Content<'static, Self::Type, Self::Form> { self.0 - .0 - .replace(|inner, emplacer| inner.as_mut_value().into_assignee(emplacer)) + .emplace_map(|inner, emplacer| inner.as_mut_value().into_assignee(emplacer, None)) } } @@ -399,69 +414,6 @@ impl DerefMut for Assignee { } } -/// A simple wrapper for a mutable reference to a value. -/// -/// Can be destructured as: `Mutable(cell): Mutable` -/// -/// If you need span information, wrap with `Spanned>`. -pub(crate) struct Mutable(pub(crate) MutableSubRcRefCell); - -impl Mutable { - pub(crate) fn into_shared(self) -> Shared { - Shared(self.0.into_shared()) - } - - #[allow(unused)] - pub(crate) fn map( - self, - value_map: impl for<'a> FnOnce(&'a mut T) -> &'a mut V, - ) -> Mutable { - Mutable(self.0.map(value_map)) - } - - /// Maps the mutable reference, returning the error and original reference on failure. - pub(crate) fn try_map( - self, - value_map: impl for<'a> FnOnce(&'a mut T) -> Result<&'a mut V, E>, - ) -> Result, (E, Mutable)> { - match self.0.try_map(value_map) { - Ok(mapped) => Ok(Mutable(mapped)), - Err((e, original)) => Err((e, Mutable(original))), - } - } - - /// Disables this mutable reference, releasing the borrow on the RefCell. - /// Returns a `DisabledMutable` which can be cloned and later re-enabled. - pub(crate) fn disable(self) -> DisabledMutable { - DisabledMutable(self.0.disable()) - } -} - -/// A disabled mutable reference that can be safely cloned and dropped. -pub(crate) struct DisabledMutable( - pub(crate) DisabledMutableSubRcRefCell, -); - -impl Clone for DisabledMutable { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl DisabledMutable { - /// Re-enables this disabled mutable reference by re-acquiring the borrow. - pub(crate) fn enable(self, span: SpanRange) -> FunctionResult> { - self.0 - .enable() - .map(Mutable) - .map_err(|_| span.ownership_error(MUTABLE_ERROR_MESSAGE)) - } - - pub(crate) fn into_shared(self) -> DisabledShared { - DisabledShared(self.0.into_shared()) - } -} - pub(crate) static MUTABLE_ERROR_MESSAGE: &str = "The variable cannot be modified as it is already being modified"; pub(crate) static SHARED_TO_MUTABLE_ERROR_MESSAGE: &str = @@ -474,116 +426,6 @@ impl Spanned { } } -impl AnyValueMutable { - pub(crate) fn new_from_owned(value: AnyValue) -> Self { - Mutable(MutableSubRcRefCell::new_from_owned(value)) - } -} - -impl IsValueContent for Mutable { - type Type = X::Type; - type Form = BeMutable; -} - -impl> IntoValueContent<'static> for Mutable -where - X::Type: IsHierarchicalType = X>, - X::Form: IsHierarchicalForm, - X::Form: LeafAsMutForm, -{ - 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 - } -} - -impl AsRef for Mutable { - fn as_ref(&self) -> &T { - &self.0 - } -} - -impl Deref for Mutable { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Mutable { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -pub(crate) type SharedValue = AnyValueShared; - -/// 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); - -#[allow(unused)] -impl Shared { - pub(crate) fn clone(this: &Shared) -> Self { - Shared(SharedSubRcRefCell::clone(&this.0)) - } - - /// Maps the shared reference, returning the error and original reference on failure. - pub(crate) fn try_map( - self, - value_map: impl for<'a> FnOnce(&'a T) -> Result<&'a V, E>, - ) -> Result, (E, Shared)> { - match self.0.try_map(value_map) { - Ok(mapped) => Ok(Shared(mapped)), - Err((e, original)) => Err((e, Shared(original))), - } - } - - pub(crate) fn map(self, value_map: impl FnOnce(&T) -> &V) -> Shared { - Shared(self.0.map(value_map)) - } - - /// Disables this shared reference, releasing the borrow on the RefCell. - /// Returns a `DisabledShared` which can be cloned and later re-enabled. - pub(crate) fn disable(self) -> DisabledShared { - DisabledShared(self.0.disable()) - } -} - -/// A disabled shared reference that can be safely cloned and dropped. -pub(crate) struct DisabledShared( - pub(crate) DisabledSharedSubRcRefCell, -); - -impl Clone for DisabledShared { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl DisabledShared { - /// Re-enables this disabled shared reference by re-acquiring the borrow. - pub(crate) fn enable(self, span: SpanRange) -> FunctionResult> { - self.0 - .enable() - .map(Shared) - .map_err(|_| span.ownership_error(SHARED_ERROR_MESSAGE)) - } -} - -pub(crate) static SHARED_ERROR_MESSAGE: &str = - "The variable cannot be read as it is already being modified"; - impl Spanned { pub(crate) fn transparent_clone(&self) -> FunctionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; @@ -591,46 +433,19 @@ impl Spanned { } } -impl AnyValueShared { - pub(crate) fn new_from_owned(value: AnyValue) -> Self { - Shared(SharedSubRcRefCell::new_from_owned(value)) - } - - pub(crate) fn infallible_clone(&self) -> AnyValue { - self.0.clone() - } -} - -impl IsValueContent for Shared { - type Type = X::Type; - type Form = BeShared; -} - -impl> IntoValueContent<'static> for Shared -where - X::Type: IsHierarchicalType = X>, - X::Form: IsHierarchicalForm, - X::Form: LeafAsRefForm, -{ - fn into_content(self) -> Content<'static, Self::Type, Self::Form> { - self.0 - .replace(|inner, emplacer| inner.as_ref_value().into_shared(emplacer)) - } -} +pub(crate) static SHARED_ERROR_MESSAGE: &str = + "The variable cannot be read as it is already being modified"; -impl AsRef for Shared { - fn as_ref(&self) -> &T { - &self.0 - } -} +// ============================================================================ +// IntoValueContent impls for the new types (formerly on Shared / Mutable) +// ============================================================================ -impl Deref for Shared { - type Target = T; +// Note: IsValueContent and IntoValueContent are implemented via QqqShared/QqqMutable +// in the forms system, since SharedReference IS the leaf type now. - fn deref(&self) -> &Self::Target { - &self.0 - } -} +// ============================================================================ +// CopyOnWrite +// ============================================================================ /// Copy-on-write value that can be either owned or shared pub(crate) struct CopyOnWrite { @@ -642,20 +457,20 @@ pub(crate) enum CopyOnWriteInner { 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), + SharedWithInfallibleCloning(SharedReference), /// 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), + SharedWithTransparentCloning(SharedReference), } impl CopyOnWrite { - pub(crate) fn shared_in_place_of_owned(shared: Shared) -> Self { + pub(crate) fn shared_in_place_of_owned(shared: SharedReference) -> Self { Self { inner: CopyOnWriteInner::SharedWithInfallibleCloning(shared), } } - pub(crate) fn shared_in_place_of_shared(shared: Shared) -> Self { + pub(crate) fn shared_in_place_of_shared(shared: SharedReference) -> Self { Self { inner: CopyOnWriteInner::SharedWithTransparentCloning(shared), } @@ -693,7 +508,7 @@ impl CopyOnWrite { pub(crate) fn map( self, - map_shared: impl FnOnce(Shared) -> FunctionResult>, + map_shared: impl FnOnce(SharedReference) -> FunctionResult>, map_owned: impl FnOnce(Owned) -> FunctionResult>, ) -> FunctionResult> { let inner = match self.inner { @@ -710,7 +525,7 @@ impl CopyOnWrite { pub(crate) fn map_into( self, - map_shared: impl FnOnce(Shared) -> U, + map_shared: impl FnOnce(SharedReference) -> U, map_owned: impl FnOnce(Owned) -> U, ) -> U { match self.inner { @@ -720,7 +535,7 @@ impl CopyOnWrite { } } - /// Disables this copy-on-write value, releasing any borrow on the RefCell. + /// Disables this copy-on-write value, releasing any borrow. /// Returns a `DisabledCopyOnWrite` which can be cloned and later re-enabled. pub(crate) fn disable(self) -> DisabledCopyOnWrite { let inner = match self.inner { @@ -743,8 +558,8 @@ pub(crate) struct DisabledCopyOnWrite { enum DisabledCopyOnWriteInner { Owned(Owned), - SharedWithInfallibleCloning(DisabledShared), - SharedWithTransparentCloning(DisabledShared), + SharedWithInfallibleCloning(InactiveSharedReference), + SharedWithTransparentCloning(InactiveSharedReference), } impl Clone for DisabledCopyOnWrite @@ -772,11 +587,11 @@ impl DisabledCopyOnWrite { pub(crate) fn enable(self, span: SpanRange) -> FunctionResult> { let inner = match self.inner { DisabledCopyOnWriteInner::Owned(owned) => CopyOnWriteInner::Owned(owned), - DisabledCopyOnWriteInner::SharedWithInfallibleCloning(shared) => { - CopyOnWriteInner::SharedWithInfallibleCloning(shared.enable(span)?) + DisabledCopyOnWriteInner::SharedWithInfallibleCloning(inactive) => { + CopyOnWriteInner::SharedWithInfallibleCloning(inactive.activate(span)?) } - DisabledCopyOnWriteInner::SharedWithTransparentCloning(shared) => { - CopyOnWriteInner::SharedWithTransparentCloning(shared.enable(span)?) + DisabledCopyOnWriteInner::SharedWithTransparentCloning(inactive) => { + CopyOnWriteInner::SharedWithTransparentCloning(inactive.activate(span)?) } }; Ok(CopyOnWrite { inner }) @@ -854,9 +669,9 @@ impl CopyOnWrite { } /// Converts to shared reference - pub(crate) fn into_shared(self) -> SharedValue { + pub(crate) fn into_shared(self, span: SpanRange) -> SharedValue { match self.inner { - CopyOnWriteInner::Owned(owned) => SharedValue::new_from_owned(owned), + CopyOnWriteInner::Owned(owned) => SharedValue::new_from_owned(owned, None, span), CopyOnWriteInner::SharedWithInfallibleCloning(shared) => shared, CopyOnWriteInner::SharedWithTransparentCloning(shared) => shared, } diff --git a/src/interpretation/refs.rs b/src/interpretation/refs.rs index 216638ef..bf8faa57 100644 --- a/src/interpretation/refs.rs +++ b/src/interpretation/refs.rs @@ -1,42 +1,63 @@ -use std::mem::transmute; - use super::*; /// A flexible type which can either be a reference to a value of type `T`, -/// or an emplaced reference from a [`Shared`]. +/// or an emplaced reference from a [`SharedReference`]. pub(crate) struct AnyRef<'a, T: ?Sized + 'static> { inner: AnyRefInner<'a, T>, } impl<'a, T: ?Sized + 'static> AnyRef<'a, T> { + /// Maps this `AnyRef` using a closure that returns a [`MappedRef`]. + /// + /// The unsafe PathExtension assertion is confined to the [`MappedRef::new`] constructor. #[allow(unused)] - pub(crate) fn map(self, f: impl for<'r> FnOnce(&'r T) -> &'r S) -> AnyRef<'a, S> { + pub(crate) fn map( + self, + f: impl for<'r> FnOnce(&'r T) -> MappedRef<'r, S>, + ) -> AnyRef<'a, S> { match self.inner { - AnyRefInner::Direct(value) => AnyRef { - inner: AnyRefInner::Direct(f(value)), - }, + AnyRefInner::Direct(value) => { + let (value, _path_extension, _span) = f(value).into_parts(); + AnyRef { + inner: AnyRefInner::Direct(value), + } + } AnyRefInner::Encapsulated(shared) => AnyRef { - inner: AnyRefInner::Encapsulated(shared.map(|x| f(x))), + inner: AnyRefInner::Encapsulated(shared.map(f)), }, } } + /// Maps this `AnyRef` using a closure that returns an optional [`MappedRef`]. + /// + /// The unsafe PathExtension assertion is confined to the [`MappedRef::new`] constructor. #[allow(unused)] - pub(crate) fn map_optional( + pub(crate) fn map_optional( self, - f: impl for<'r> FnOnce(&'r T) -> Option<&'r S>, + f: impl for<'r> FnOnce(&'r T) -> Option>, ) -> Option> { Some(match self.inner { - AnyRefInner::Direct(value) => AnyRef { - inner: AnyRefInner::Direct(f(value)?), - }, + AnyRefInner::Direct(value) => { + let (value, _path_extension, _span) = f(value)?.into_parts(); + AnyRef { + inner: AnyRefInner::Direct(value), + } + } AnyRefInner::Encapsulated(shared) => AnyRef { - inner: AnyRefInner::Encapsulated(shared.map_optional(f)?), + inner: AnyRefInner::Encapsulated(shared.emplace_map(|input, emplacer| match f( + input, + ) { + Some(mapped) => Some(emplacer.emplace(mapped)), + None => { + let _ = emplacer.revert(); + None + } + })?), }, }) } - pub(crate) fn replace( + pub(crate) fn emplace_map( self, f: impl for<'e> FnOnce(&'e T, &mut AnyRefEmplacer<'a, 'e, T>) -> O, ) -> O { @@ -60,26 +81,37 @@ pub(crate) struct AnyRefEmplacer<'a, 'e: 'a, T: 'static + ?Sized> { } 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) - } + pub(crate) fn revert(&mut self) -> AnyRef<'a, T> { + self.inner.take().expect("Emplacer already consumed") } - // SAFETY: - // * The caller must ensure that the value's lifetime is derived from the original content - pub(crate) unsafe fn emplace_unchecked( + /// Emplaces a mapped shared reference, consuming the emplacer's reference tracking. + /// + /// This is safe because all preconditions (correct PathExtension and valid reference + /// derivation) are validated by the [`MappedRef`] constructor. + pub(crate) fn emplace( &mut self, - value: &V, + mapped: MappedRef<'e, V>, ) -> AnyRef<'a, V> { - self.inner + let any_ref = 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) }) + .expect("You can only emplace to create a new AnyRef value once"); + let (value, path_extension, span) = mapped.into_parts(); + match any_ref.inner { + AnyRefInner::Direct(_) => AnyRef { + // 'e: 'a is guaranteed by the struct constraint, so coercion is valid + inner: AnyRefInner::Direct(value), + }, + AnyRefInner::Encapsulated(shared) => AnyRef { + inner: AnyRefInner::Encapsulated(shared.emplace_map(|_, emplacer| { + // SAFETY: The value's lifetime ('e) matches the SharedEmplacer's lifetime + // since both are derived from the same underlying data + let remapped = unsafe { MappedRef::new_unchecked(value, path_extension, span) }; + emplacer.emplace(remapped) + })), + }, + } } } @@ -107,15 +139,15 @@ impl<'a, T: ?Sized> ToSpannedRef<'a> for &'a T { } } -impl<'a, T: ?Sized> From> for AnyRef<'a, T> { - fn from(value: Shared) -> Self { +impl<'a, T: ?Sized> From> for AnyRef<'a, T> { + fn from(value: SharedReference) -> Self { Self { - inner: AnyRefInner::Encapsulated(value.0), + inner: AnyRefInner::Encapsulated(value), } } } -impl ToSpannedRef<'static> for Shared { +impl ToSpannedRef<'static> for SharedReference { type Target = T; fn into_ref(self) -> AnyRef<'static, Self::Target> { self.into() @@ -127,7 +159,7 @@ impl ToSpannedRef<'static> for Shared { enum AnyRefInner<'a, T: 'static + ?Sized> { Direct(&'a T), - Encapsulated(SharedSubRcRefCell), + Encapsulated(SharedReference), } impl<'a, T: 'static + ?Sized> Deref for AnyRef<'a, T> { @@ -142,7 +174,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 emplaced reference from a [`Mutable`]. +/// or an emplaced reference from a [`MutableReference`]. pub(crate) struct AnyMut<'a, T: 'static + ?Sized> { inner: AnyMutInner<'a, T>, } @@ -156,81 +188,143 @@ impl<'a, T: ?Sized> From<&'a mut T> for AnyMut<'a, T> { } impl<'a, T: ?Sized + 'static> AnyMut<'a, T> { + /// Maps this `AnyMut` using a closure that returns a [`MappedMut`]. + /// + /// The unsafe PathExtension assertion is confined to the [`MappedMut::new`] constructor. #[allow(unused)] - pub(crate) fn map( + pub(crate) fn map( self, - f: impl for<'r> FnOnce(&'r mut T) -> &'r mut S, + f: impl for<'r> FnOnce(&'r mut T) -> MappedMut<'r, S>, ) -> AnyMut<'a, S> { match self.inner { - AnyMutInner::Direct(value) => AnyMut { - inner: AnyMutInner::Direct(f(value)), - }, + AnyMutInner::Direct(value) => { + let (value, _path_extension, _span) = f(value).into_parts(); + AnyMut { + inner: AnyMutInner::Direct(value), + } + } AnyMutInner::Encapsulated(mutable) => AnyMut { - inner: AnyMutInner::Encapsulated(mutable.map(|x| f(x))), + inner: AnyMutInner::Encapsulated(mutable.map(f)), }, } } + /// Maps this `AnyMut` using a closure that returns an optional [`MappedMut`]. + /// + /// The unsafe PathExtension assertion is confined to the [`MappedMut::new`] constructor. #[allow(unused)] - pub(crate) fn map_optional( + pub(crate) fn map_optional( self, - f: impl for<'r> FnOnce(&'r mut T) -> Option<&'r mut S>, + f: impl for<'r> FnOnce(&'r mut T) -> Option>, ) -> Option> { Some(match self.inner { - AnyMutInner::Direct(value) => AnyMut { - inner: AnyMutInner::Direct(f(value)?), - }, + AnyMutInner::Direct(value) => { + let (value, _path_extension, _span) = f(value)?.into_parts(); + AnyMut { + inner: AnyMutInner::Direct(value), + } + } AnyMutInner::Encapsulated(mutable) => AnyMut { - inner: AnyMutInner::Encapsulated(mutable.map_optional(f)?), + inner: AnyMutInner::Encapsulated(mutable.emplace_map( + |input, emplacer| match f(input) { + Some(mapped) => Some(emplacer.emplace(mapped)), + None => { + let _ = emplacer.revert(); + None + } + }, + )?), }, }) } - pub(crate) fn replace( - mut self, + pub(crate) fn emplace_map( + 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, - ) + match self.inner { + AnyMutInner::Direct(direct_mut) => { + // Convert to raw pointer to avoid &mut aliasing: the emplacer + // stores the raw pointer, so no second &mut T exists. + let raw_ptr = direct_mut as *mut T; + let mut emplacer = AnyMutEmplacer { + inner: Some(AnyMutEmplacerState::Direct(raw_ptr, PhantomData)), + encapsulation_lifetime: std::marker::PhantomData, + }; + // SAFETY: raw_ptr was derived from a valid &'a mut T, and the original + // reference was consumed by converting to *mut T. + f(unsafe { &mut *raw_ptr }, &mut emplacer) + } + AnyMutInner::Encapsulated(mut mutable) => { + // MutableReference internally stores NonNull (a raw pointer), + // not &mut T, so there is no aliasing issue. + let raw_ptr = (&mut *mutable) as *mut T; + let mut emplacer = AnyMutEmplacer { + inner: Some(AnyMutEmplacerState::Encapsulated(mutable)), + encapsulation_lifetime: std::marker::PhantomData, + }; + // SAFETY: raw_ptr was derived from the MutableReference which + // uses NonNull internally (not &mut), so no aliasing occurs. + f(unsafe { &mut *raw_ptr }, &mut emplacer) + } + } } } pub(crate) struct AnyMutEmplacer<'a, 'e: 'a, T: 'static + ?Sized> { - inner: Option>, + inner: Option>, encapsulation_lifetime: std::marker::PhantomData<&'e ()>, } +/// Stores either a raw pointer (for Direct) or a MutableReference (for Encapsulated), +/// avoiding &mut aliasing that would occur if we stored the full AnyMut. +enum AnyMutEmplacerState<'a, T: 'static + ?Sized> { + Direct(*mut T, PhantomData<&'a mut T>), + Encapsulated(MutableReference), +} + 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) + pub(crate) fn revert(&mut self) -> AnyMut<'a, T> { + let state = self.inner.take().expect("Emplacer already consumed"); + match state { + AnyMutEmplacerState::Direct(ptr, _) => AnyMut { + // SAFETY: ptr was derived from a valid &'a mut T and no other + // &mut T currently exists (the one passed to the closure has ended). + inner: AnyMutInner::Direct(unsafe { &mut *ptr }), + }, + AnyMutEmplacerState::Encapsulated(mutable) => AnyMut { + inner: AnyMutInner::Encapsulated(mutable), + }, } } - // SAFETY: - // * The caller must ensure that the value's lifetime is derived from the original content - pub(crate) unsafe fn emplace_unchecked( + /// Emplaces a mapped mutable reference, consuming the emplacer's reference tracking. + /// + /// This is safe because all preconditions (correct PathExtension and valid reference + /// derivation) are validated by the [`MappedMut`] constructor. + pub(crate) fn emplace( &mut self, - value: &mut V, + mapped: MappedMut<'e, V>, ) -> AnyMut<'a, V> { - self.inner + let state = 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) }) + .expect("You can only emplace to create a new AnyMut value once"); + let (value, path_extension, span) = mapped.into_parts(); + match state { + AnyMutEmplacerState::Direct(_, _) => AnyMut { + // 'e: 'a is guaranteed by the struct constraint, so coercion is valid + inner: AnyMutInner::Direct(value), + }, + AnyMutEmplacerState::Encapsulated(mutable) => AnyMut { + inner: AnyMutInner::Encapsulated(mutable.emplace_map(|_, emplacer| { + // SAFETY: The value's lifetime ('e) matches the MutableEmplacer's lifetime + // since both are derived from the same underlying data + let remapped = unsafe { MappedMut::new_unchecked(value, path_extension, span) }; + emplacer.emplace(remapped) + })), + }, + } } } @@ -248,17 +342,17 @@ impl<'a, T: ?Sized + 'static> IntoAnyMut<'a> for &'a mut T { } } -impl<'a, T: ?Sized> From> for AnyMut<'a, T> { - fn from(value: Mutable) -> Self { +impl<'a, T: ?Sized> From> for AnyMut<'a, T> { + fn from(value: MutableReference) -> Self { Self { - inner: AnyMutInner::Encapsulated(value.0), + inner: AnyMutInner::Encapsulated(value), } } } enum AnyMutInner<'a, T: 'static + ?Sized> { Direct(&'a mut T), - Encapsulated(MutableSubRcRefCell), + Encapsulated(MutableReference), } impl<'a, T: 'static + ?Sized> Deref for AnyMut<'a, T> { diff --git a/src/interpretation/variable.rs b/src/interpretation/variable.rs index dd67fc24..2c05e064 100644 --- a/src/interpretation/variable.rs +++ b/src/interpretation/variable.rs @@ -68,7 +68,11 @@ impl VariableDefinition { pub(crate) fn define(&self, interpreter: &mut Interpreter, value_source: impl IntoAnyValue) { interpreter.define_variable( self.id, - VariableContent::Referenceable(Rc::new(RefCell::new(value_source.into_any_value()))), + VariableContent::Referenceable(Referenceable::new( + value_source.into_any_value(), + Some(self.ident.to_string()), + self.ident.span_range(), + )), ); } } diff --git a/src/misc/dynamic_references/mod.rs b/src/misc/dynamic_references/mod.rs index 5efc3ae3..c07ac467 100644 --- a/src/misc/dynamic_references/mod.rs +++ b/src/misc/dynamic_references/mod.rs @@ -81,8 +81,6 @@ //! Was based off an `Rc>` which we could use to create Active/Mutable references, and deactivate them. //! However, it didn't respect (a.1) and it didn't permit mutiple mutable references on different paths. -// TODO[references]: Remove once integrated -#![allow(unused)] mod mutable_reference; mod reference_core; mod referenceable; @@ -93,7 +91,6 @@ use crate::internal_prelude::*; use reference_core::*; use slotmap::{new_key_type, SlotMap}; use std::cell::UnsafeCell; -use std::cmp::Ordering; use std::mem::ManuallyDrop; use std::ptr::NonNull; diff --git a/src/misc/dynamic_references/mutable_reference.rs b/src/misc/dynamic_references/mutable_reference.rs index 329deb1d..c3d17e28 100644 --- a/src/misc/dynamic_references/mutable_reference.rs +++ b/src/misc/dynamic_references/mutable_reference.rs @@ -1,3 +1,5 @@ +use std::mem::transmute; + use super::*; pub(crate) struct MutableReference(pub(super) ReferenceCore); @@ -12,7 +14,7 @@ impl MutableReference { /// a structure arbitrarily. pub(crate) fn emplace_map( mut self, - f: impl for<'e> FnOnce(&'e mut T, &mut MutableEmplacerV2<'e, T>) -> O, + f: impl for<'e> FnOnce(&'e mut T, &mut MutableEmplacer<'e, T>) -> O, ) -> O { // SAFETY: The validity + safety invariants are upheld by `ReferenceableCore` // ... assuming this id is marked as a mutable reference for the duration. @@ -20,40 +22,73 @@ impl MutableReference { // - Delegating to the created MutableReference if it is emplaced // - Surviving until Drop at the end of this method if it is not emplaced let copied_mut = unsafe { self.0.pointer.as_mut() }; - let mut emplacer = MutableEmplacerV2(self.0.into_emplacer()); + let mut emplacer = MutableEmplacer(self.0.into_emplacer()); f(copied_mut, &mut emplacer) } - /// SAFETY: - /// - The caller must ensure that the PathExtension is correct - /// (an overly-specific PathExtension may cause safety issues) - pub(crate) unsafe fn map( + /// Maps this mutable reference using a closure that returns a [`MappedMut`]. + /// + /// The unsafe PathExtension assertion is confined to the [`MappedMut::new`] constructor, + /// making this method itself safe. + pub(crate) fn map( self, - f: impl FnOnce(&mut T) -> &mut V, - path_extension: PathExtension, - new_span: SpanRange, + f: impl for<'r> FnOnce(&'r mut T) -> MappedMut<'r, V>, ) -> MutableReference { - self.emplace_map(move |input, emplacer| { - emplacer.emplace(f(input), path_extension, new_span) - }) + self.emplace_map(move |input, emplacer| emplacer.emplace(f(input))) } - /// SAFETY: - /// - The caller must ensure that the PathExtension is correct - /// (an overly-specific PathExtension may cause safety issues) - pub(crate) unsafe fn try_map( + /// Fallible version of [`map`](Self::map) that returns the original reference on error. + /// + /// The unsafe PathExtension assertion is confined to the [`MappedMut::new`] constructor. + pub(crate) fn try_map( self, - f: impl FnOnce(&mut T) -> Result<&mut V, E>, - path_extension: PathExtension, - new_span: SpanRange, + f: impl for<'r> FnOnce(&'r mut T) -> Result, E>, ) -> Result, (E, MutableReference)> { self.emplace_map(|input, emplacer| match f(input) { - Ok(output) => Ok(emplacer.emplace(output, path_extension, new_span)), + Ok(mapped) => Ok(emplacer.emplace(mapped)), Err(e) => Err((e, emplacer.revert())), }) } } +impl MutableReference { + /// Creates a new MutableReference from an owned value, wrapping it in a Referenceable. + pub(crate) fn new_from_owned( + value: AnyValue, + root_name: Option, + span_range: SpanRange, + ) -> Self { + let referenceable = Referenceable::new(value, root_name, span_range); + referenceable + .new_active_mutable(span_range) + .expect("Freshly created referenceable must be borrowable as mutable") + } +} + +impl MutableReference { + /// Disables this mutable reference (bridge for old `disable()` API). + /// Equivalent to `deactivate()` in the new naming. + pub(crate) fn disable(self) -> InactiveMutableReference { + self.deactivate() + } + + /// Converts this mutable reference into a shared reference. + /// Bridge for old `into_shared()` API. + pub(crate) fn into_shared(self) -> SharedReference { + let span = self + .0 + .core + .data_mut() + .for_reference(self.0.id) + .creation_span; + let inactive = self.deactivate(); + let inactive_shared = inactive.into_shared(); + inactive_shared + .activate(span) + .expect("Converting mutable to shared should always succeed since we just released the mutable borrow") + } +} + impl Deref for MutableReference { type Target = T; @@ -67,6 +102,18 @@ impl Deref for MutableReference { } } +impl AsRef for MutableReference { + fn as_ref(&self) -> &T { + self + } +} + +impl AsMut for MutableReference { + fn as_mut(&mut self) -> &mut T { + self + } +} + impl DerefMut for MutableReference { fn deref_mut(&mut self) -> &mut Self::Target { // SAFETY: See the rustdoc on dynamic_references/mod.rs for full details. @@ -78,15 +125,20 @@ impl DerefMut for MutableReference { } } -#[derive(Clone)] pub(crate) struct InactiveMutableReference(pub(super) ReferenceCore); +impl Clone for InactiveMutableReference { + fn clone(&self) -> Self { + InactiveMutableReference(self.0.clone()) + } +} + impl InactiveMutableReference { - pub(crate) fn activate(self) -> FunctionResult> { + pub(crate) fn activate(self, span: SpanRange) -> FunctionResult> { self.0 .core .data_mut() - .activate_mutable_reference(self.0.id)?; + .activate_mutable_reference(self.0.id, span)?; Ok(MutableReference(self.0)) } @@ -99,43 +151,73 @@ impl InactiveMutableReference { } } -pub(crate) struct MutableEmplacerV2<'e, T: ?Sized>(EmplacerCore<'e, T>); +/// A mapped mutable reference bundled with its [`PathExtension`] and span. +/// +/// Constructing this is unsafe because the caller must ensure the +/// [`PathExtension`] correctly describes the relationship between the +/// source and mapped reference. +pub(crate) struct MappedMut<'a, V: ?Sized> { + value: &'a mut V, + path_extension: PathExtension, + span: Option, +} -impl<'e, T: ?Sized> MutableEmplacerV2<'e, T> { - pub(crate) fn revert(&mut self) -> MutableReference { - MutableReference(self.0.revert()) +impl<'a, V: ?Sized> MappedMut<'a, V> { + /// SAFETY: The caller must ensure that the PathExtension correctly describes + /// the navigation from the source reference to this mapped reference. + /// An overly-specific PathExtension may cause safety issues. + pub(crate) unsafe fn new( + value: &'a mut V, + path_extension: PathExtension, + span: SpanRange, + ) -> Self { + Self { + value, + path_extension, + span: Some(span), + } } - /// SAFETY: - /// - The caller must ensure that the PathExtension is correct - /// (an overly-specific PathExtension may cause safety issues) - pub(crate) unsafe fn emplace( - &mut self, - value: &'e mut V, + /// SAFETY: In addition to the safety requirements of [`new`](Self::new), the caller + /// must ensure that the reference lifetime is valid for the target lifetime `'a`. + /// This is needed when the compiler cannot prove the lifetime relationship + /// (e.g. in LeafMapper implementations where `'l` and `'e` cannot be unified). + pub(crate) unsafe fn new_unchecked<'any>( + value: &'any mut V, path_extension: PathExtension, - new_span: SpanRange, - ) -> MutableReference { - 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, path_extension, new_span) + span: Option, + ) -> Self { + Self { + value: unsafe { transmute::<&'any mut V, &'a mut V>(value) }, + path_extension, + span, } } - /// SAFETY: - /// - The caller must ensure that the value's lifetime is derived from the original content - /// - The caller must ensure that the PathExtension is correct - pub(crate) unsafe fn emplace_unchecked( + pub(crate) fn into_parts(self) -> (&'a mut V, PathExtension, Option) { + (self.value, self.path_extension, self.span) + } +} + +pub(crate) struct MutableEmplacer<'e, T: ?Sized>(EmplacerCore<'e, T>); + +impl<'e, T: ?Sized> MutableEmplacer<'e, T> { + pub(crate) fn revert(&mut self) -> MutableReference { + MutableReference(self.0.revert()) + } + + /// Emplaces a mapped mutable reference, consuming the emplacer's reference tracking. + /// + /// This is safe because all preconditions (correct PathExtension and valid reference + /// derivation) are validated by the [`MappedMut`] constructor. + pub(crate) fn emplace( &mut self, - value: &mut V, - path_extension: PathExtension, - new_span: SpanRange, + mapped: MappedMut<'e, V>, ) -> MutableReference { - // SAFETY: The pointer is from a reference so non-null + let (value, path_extension, span) = mapped.into_parts(); + // SAFETY: The pointer is from a valid reference (guaranteed by MappedMut constructor), + // and the PathExtension was validated by the MappedMut constructor. let pointer = unsafe { NonNull::new_unchecked(value as *mut V) }; - // SAFETY: - // - The caller ensures that the reference is derived from the original content - // - The caller ensures that the PathExtension is correct - unsafe { MutableReference(self.0.emplace_unchecked(pointer, path_extension, new_span)) } + unsafe { MutableReference(self.0.emplace_unchecked(pointer, path_extension, span)) } } } diff --git a/src/misc/dynamic_references/reference_core.rs b/src/misc/dynamic_references/reference_core.rs index 52eed74e..7285f914 100644 --- a/src/misc/dynamic_references/reference_core.rs +++ b/src/misc/dynamic_references/reference_core.rs @@ -97,7 +97,7 @@ impl<'e, T: ?Sized> EmplacerCore<'e, T> { &mut self, pointer: NonNull, path_extension: PathExtension, - new_span: SpanRange, + new_span: Option, ) -> ReferenceCore { let emplacer_core = self.take(); let id = emplacer_core.id; diff --git a/src/misc/dynamic_references/referenceable.rs b/src/misc/dynamic_references/referenceable.rs index cf45a735..31de0f01 100644 --- a/src/misc/dynamic_references/referenceable.rs +++ b/src/misc/dynamic_references/referenceable.rs @@ -1,12 +1,17 @@ -use std::process::Child; - use super::*; +#[derive(Clone)] pub(crate) struct Referenceable { core: Rc>, } impl Referenceable { + pub(crate) fn new(root: AnyValue, root_name: Option, root_span: SpanRange) -> Self { + Self { + core: Rc::new(ReferenceableCore::new(root, root_name, root_span)), + } + } + pub(crate) fn new_inactive_shared(&self) -> InactiveSharedReference { InactiveSharedReference(ReferenceCore::new_root( self.core.clone(), @@ -20,6 +25,28 @@ impl Referenceable { ReferenceKind::InactiveMutable, )) } + + pub(crate) fn new_active_shared( + &self, + span: SpanRange, + ) -> FunctionResult> { + self.new_inactive_shared().activate(span) + } + + pub(crate) fn new_active_mutable( + &self, + span: SpanRange, + ) -> FunctionResult> { + self.new_inactive_mutable().activate(span) + } + + /// Attempts to unwrap the inner value if this is the sole owner (no outstanding references). + pub(crate) fn try_into_inner(self) -> Result { + match Rc::try_unwrap(self.core) { + Ok(core) => Ok(core.into_inner()), + Err(core) => Err(Self { core }), + } + } } pub(super) struct ReferenceableCore { @@ -29,7 +56,7 @@ pub(super) struct ReferenceableCore { } impl ReferenceableCore { - pub(super) fn new(root: T, root_name: String, root_span: SpanRange) -> Self { + pub(super) fn new(root: T, root_name: Option, root_span: SpanRange) -> Self { Self { root: UnsafeCell::new(root), data: RefCell::new(ReferenceableData { @@ -47,13 +74,13 @@ impl ReferenceableCore { } } - pub(super) fn data(&self) -> Ref<'_, ReferenceableData> { - self.data.borrow() - } - pub(super) fn data_mut(&self) -> RefMut<'_, ReferenceableData> { self.data.borrow_mut() } + + pub(super) fn into_inner(self) -> T { + self.root.into_inner() + } } new_key_type! { @@ -62,7 +89,7 @@ new_key_type! { pub(super) struct ReferenceableData { // Could be in Referencable Core, but having it here makes the error message API easier - root_name: String, + root_name: Option, // Could be in Referencable Core, but having it here makes the error message API easier root_span: SpanRange, arena: SlotMap, @@ -115,7 +142,9 @@ impl ReferenceableData { pub(super) fn activate_mutable_reference( &mut self, id: LocalReferenceId, + span: SpanRange, ) -> FunctionResult<()> { + self.for_reference_mut(id).creation_span = span; let data = self.for_reference(id); // Perform checks as per the module doc on `dynamic_references` for (other_id, other_data) in self.arena.iter() { @@ -128,14 +157,14 @@ impl ReferenceableData { Some(reason) => reason, None => continue, }; - let mut error_message = - "Cannot create mutable reference because it clashes with another reference: " - .to_string(); - error_message.push_str(error_reason); - let _ = write!(error_message, "This reference-: "); - self.display_path(&mut error_message, id, Some(ReferenceKind::ActiveMutable)); - let _ = write!(error_message, "Other reference: "); - self.display_path(&mut error_message, other_id, None); + let mut error_message = String::new(); + let _ = write!(error_message, "Cannot create a mutable reference because it clashes with an existing reference:"); + let _ = write!(error_message, "\nThis reference : "); + let _ = + self.display_path(&mut error_message, id, Some(ReferenceKind::ActiveMutable)); + let _ = write!(error_message, "\nOther reference: "); + let _ = self.display_path(&mut error_message, other_id, None); + let _ = write!(error_message, "\nReason : {}\n", error_reason); return data.creation_span.ownership_err(error_message); } } @@ -152,7 +181,12 @@ impl ReferenceableData { Ok(()) } - pub(super) fn activate_shared_reference(&mut self, id: LocalReferenceId) -> FunctionResult<()> { + pub(super) fn activate_shared_reference( + &mut self, + id: LocalReferenceId, + span: SpanRange, + ) -> FunctionResult<()> { + self.for_reference_mut(id).creation_span = span; let data = self.for_reference(id); // Perform checks as per the module doc on `dynamic_references` for (other_id, other_data) in self.arena.iter() { @@ -165,14 +199,14 @@ impl ReferenceableData { Some(reason) => reason, None => continue, }; - let mut error_message = - "Cannot create shared reference because it clashes with a mutable reference: " - .to_string(); - error_message.push_str(error_reason); - let _ = write!(error_message, "This reference-: "); - self.display_path(&mut error_message, id, Some(ReferenceKind::ActiveShared)); - let _ = write!(error_message, "Other reference: "); - self.display_path(&mut error_message, other_id, None); + let mut error_message = String::new(); + let _ = write!(error_message, "Cannot create a shared reference because it clashes with an existing mutable reference:"); + let _ = write!(error_message, "\nThis reference : "); + let _ = + self.display_path(&mut error_message, id, Some(ReferenceKind::ActiveShared)); + let _ = write!(error_message, "\nOther reference: "); + let _ = self.display_path(&mut error_message, other_id, None); + let _ = write!(error_message, "\nReason : {}\n", error_reason); return data.creation_span.ownership_err(error_message); } } @@ -190,11 +224,13 @@ impl ReferenceableData { &mut self, id: LocalReferenceId, path_extension: PathExtension, - new_span: SpanRange, + new_span: Option, ) { let data = self.for_reference_mut(id); - data.creation_span = new_span; - let mut last_path_part = data.path.parts.last_mut().expect("path is non-empty"); + if let Some(span) = new_span { + data.creation_span = span; + } + let last_path_part = data.path.parts.last_mut().expect("path is non-empty"); match (last_path_part, path_extension) { (last_path_part, PathExtension::Child(specifier, child_bound_as)) => { let parent_type = specifier.bound_type_kind(); @@ -249,7 +285,10 @@ impl ReferenceableData { ReferenceKind::ActiveShared => f.write_str("[*active*] &")?, ReferenceKind::ActiveMutable => f.write_str("[*active*] &mut ")?, } - f.write_str(&self.root_name)?; + match self.root_name.as_ref() { + Some(name) => f.write_str(name)?, + None => f.write_str("[root]")?, + } for part in data.path.parts.iter() { match part { PathPart::Value { bound_as } => { @@ -282,6 +321,8 @@ impl ReferenceableData { pub(super) struct TrackedReference { pub(super) path: ReferencePath, pub(super) reference_kind: ReferenceKind, + /// Storing this span isn't strictly necessary for now... + /// But it's added for a future world where the diagnostic API is stabilized and we can add a diagnostic onto the clashing reference. pub(super) creation_span: SpanRange, } @@ -346,23 +387,27 @@ impl PathComparison { Some(match self { PathComparison::Divergent => return None, PathComparison::Overlapping => { - "they overlap, so mutation may invalidate the other reference" + "mutation may invalidate the other overlapping reference" } PathComparison::RightIsDescendent => { "mutation may invalidate the other descendent reference" } - PathComparison::ReferencesEqual(TypeBindingComparison::RightDerivesFromLeft) => { - "mutation may invalidate the other reference with more specific type" - } - PathComparison::ReferencesEqual(TypeBindingComparison::Incomparable) => { + PathComparison::ReferencesEqual(TypeBindingComparison::RightIsSubtypeOfLeft) + | PathComparison::ReferencesEqual( + TypeBindingComparison::RightDerivesFromLeftButIsNotSubtype, + ) + | PathComparison::ReferencesEqual( + TypeBindingComparison::LeftDerivesFromRightButIsNotSubtype, + ) + | PathComparison::ReferencesEqual(TypeBindingComparison::Incomparable) => { "mutation may invalidate the other reference with an incompatible type" } - // Activated reference is descendent of existing reference + // Mutable reference is a descendent of the other reference PathComparison::ReferencesEqual(TypeBindingComparison::Equal) - | PathComparison::ReferencesEqual(TypeBindingComparison::LeftDerivesFromRight) + | PathComparison::ReferencesEqual(TypeBindingComparison::LeftIsSubtypeOfRight) | PathComparison::LeftIsDescendent => { if other_is_active { - "the mutable reference is observable from the other reference, which breaks aliasing rules" + "mutation may be observed from the other active reference, which breaks aliasing rules" } else { return None; } diff --git a/src/misc/dynamic_references/shared_reference.rs b/src/misc/dynamic_references/shared_reference.rs index 5a783fdf..245ba601 100644 --- a/src/misc/dynamic_references/shared_reference.rs +++ b/src/misc/dynamic_references/shared_reference.rs @@ -1,9 +1,16 @@ +use std::mem::transmute; + use super::*; -#[derive(Clone)] pub(crate) struct SharedReference(pub(super) ReferenceCore); -impl SharedReference { +impl Clone for SharedReference { + fn clone(&self) -> Self { + SharedReference(self.0.clone()) + } +} + +impl SharedReference { pub(crate) fn deactivate(self) -> InactiveSharedReference { self.0.core.data_mut().deactivate_reference(self.0.id); InactiveSharedReference(self.0) @@ -13,7 +20,7 @@ impl SharedReference { /// a structure arbitrarily. pub(crate) fn emplace_map( self, - f: impl for<'e> FnOnce(&'e T, &mut SharedEmplacerV2<'e, T>) -> O, + f: impl for<'e> FnOnce(&'e T, &mut SharedEmplacer<'e, T>) -> O, ) -> O { // SAFETY: The validity + safety invariants are upheld by `ReferenceableCore` // ... assuming this id is marked as a shared reference for the duration. @@ -21,41 +28,64 @@ impl SharedReference { // - Delegating to the created SharedReference if it is emplaced // - Surviving until Drop at the end of this method if it is not emplaced let copied_ref = unsafe { self.0.pointer.as_ref() }; - let mut emplacer = SharedEmplacerV2(self.0.into_emplacer()); + let mut emplacer = SharedEmplacer(self.0.into_emplacer()); f(copied_ref, &mut emplacer) } - /// SAFETY: - /// - The caller must ensure that the PathExtension is correct - /// (an overly-specific PathExtension may cause safety issues) - pub(crate) unsafe fn map( + /// Maps this shared reference using a closure that returns a [`MappedRef`]. + /// + /// The unsafe PathExtension assertion is confined to the [`MappedRef::new`] constructor, + /// making this method itself safe. + pub(crate) fn map( self, - f: impl FnOnce(&T) -> &V, - path_extension: PathExtension, - new_span: SpanRange, + f: impl for<'r> FnOnce(&'r T) -> MappedRef<'r, V>, ) -> SharedReference { - self.emplace_map(move |input, emplacer| { - emplacer.emplace(f(input), path_extension, new_span) - }) + self.emplace_map(move |input, emplacer| emplacer.emplace(f(input))) } - /// SAFETY: - /// - The caller must ensure that the PathExtension is correct - /// (an overly-specific PathExtension may cause safety issues) - pub(crate) unsafe fn try_map( + /// Fallible version of [`map`](Self::map) that returns the original reference on error. + /// + /// The unsafe PathExtension assertion is confined to the [`MappedRef::new`] constructor. + pub(crate) fn try_map( self, - f: impl FnOnce(&T) -> Result<&V, E>, - path_extension: PathExtension, - new_span: SpanRange, + f: impl for<'r> FnOnce(&'r T) -> Result, E>, ) -> Result, (E, SharedReference)> { self.emplace_map(|input, emplacer| match f(input) { - Ok(output) => Ok(emplacer.emplace(output, path_extension, new_span)), + Ok(mapped) => Ok(emplacer.emplace(mapped)), Err(e) => Err((e, emplacer.revert())), }) } } -impl Deref for SharedReference { +impl SharedReference { + /// Creates a new SharedReference from an owned value, wrapping it in a Referenceable. + /// Uses a placeholder name and span for the Referenceable root. + pub(crate) fn new_from_owned( + value: AnyValue, + root_name: Option, + span_range: SpanRange, + ) -> Self { + let referenceable = Referenceable::new(value, root_name, span_range); + referenceable + .new_active_shared(span_range) + .expect("Freshly created referenceable must be borrowable as shared") + } + + /// Clones the inner value. This is infallible because AnyValue always supports clone. + pub(crate) fn infallible_clone(&self) -> AnyValue { + (**self).clone() + } +} + +impl SharedReference { + /// Disables this shared reference (bridge for old `disable()` API). + /// Equivalent to `deactivate()` in the new naming. + pub(crate) fn disable(self) -> InactiveSharedReference { + self.deactivate() + } +} + +impl Deref for SharedReference { type Target = T; fn deref(&self) -> &Self::Target { @@ -68,56 +98,93 @@ impl Deref for SharedReference { } } -#[derive(Clone)] +impl AsRef for SharedReference { + fn as_ref(&self) -> &T { + self + } +} + pub(crate) struct InactiveSharedReference(pub(super) ReferenceCore); +impl Clone for InactiveSharedReference { + fn clone(&self) -> Self { + InactiveSharedReference(self.0.clone()) + } +} + impl InactiveSharedReference { - pub(crate) fn activate(self) -> FunctionResult> { + pub(crate) fn activate(self, span: SpanRange) -> FunctionResult> { self.0 .core .data_mut() - .activate_shared_reference(self.0.id)?; + .activate_shared_reference(self.0.id, span)?; Ok(SharedReference(self.0)) } } -pub(crate) struct SharedEmplacerV2<'e, T: ?Sized>(EmplacerCore<'e, T>); +/// A mapped shared reference bundled with its [`PathExtension`] and span. +/// +/// Constructing this is unsafe because the caller must ensure the +/// [`PathExtension`] correctly describes the relationship between the +/// source and mapped reference. +pub(crate) struct MappedRef<'a, V: ?Sized> { + value: &'a V, + path_extension: PathExtension, + span: Option, +} -impl<'e, T: ?Sized> SharedEmplacerV2<'e, T> { - pub(crate) fn revert(&mut self) -> SharedReference { - SharedReference(self.0.revert()) +impl<'a, V: ?Sized> MappedRef<'a, V> { + /// SAFETY: The caller must ensure that the PathExtension correctly describes + /// the navigation from the source reference to this mapped reference. + /// An overly-specific PathExtension may cause safety issues. + pub(crate) unsafe fn new(value: &'a V, path_extension: PathExtension, span: SpanRange) -> Self { + Self { + value, + path_extension, + span: Some(span), + } } - /// SAFETY: - /// - The caller must ensure that the PathExtension is correct - /// (an overly-specific PathExtension may cause safety issues) - pub(crate) unsafe fn emplace( - &mut self, - value: &'e V, + /// SAFETY: In addition to the safety requirements of [`new`](Self::new), the caller + /// must ensure that the reference lifetime is valid for the target lifetime `'a`. + /// This is needed when the compiler cannot prove the lifetime relationship + /// (e.g. in LeafMapper implementations where `'l` and `'e` cannot be unified). + pub(crate) unsafe fn new_unchecked<'any>( + value: &'any V, path_extension: PathExtension, - new_span: SpanRange, - ) -> SharedReference { - 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, path_extension, new_span) + span: Option, + ) -> Self { + Self { + value: unsafe { transmute::<&'any V, &'a V>(value) }, + path_extension, + span, } } - /// SAFETY: - /// - The caller must ensure that the value's lifetime is derived from the original content - /// - The caller must ensure that the PathExtension is correct - pub(crate) unsafe fn emplace_unchecked( + pub(crate) fn into_parts(self) -> (&'a V, PathExtension, Option) { + (self.value, self.path_extension, self.span) + } +} + +pub(crate) struct SharedEmplacer<'e, T: ?Sized>(EmplacerCore<'e, T>); + +impl<'e, T: ?Sized> SharedEmplacer<'e, T> { + pub(crate) fn revert(&mut self) -> SharedReference { + SharedReference(self.0.revert()) + } + + /// Emplaces a mapped shared reference, consuming the emplacer's reference tracking. + /// + /// This is safe because all preconditions (correct PathExtension and valid reference + /// derivation) are validated by the [`MappedRef`] constructor. + pub(crate) fn emplace( &mut self, - value: &V, - path_extension: PathExtension, - new_span: SpanRange, + mapped: MappedRef<'e, V>, ) -> SharedReference { - // SAFETY: The pointer is from a reference so non-null + let (value, path_extension, span) = mapped.into_parts(); + // SAFETY: The pointer is from a valid reference (guaranteed by MappedRef constructor), + // and the PathExtension was validated by the MappedRef constructor. let pointer = unsafe { NonNull::new_unchecked(value as *const V as *mut V) }; - // SAFETY: - // - The caller ensures that the reference is derived from the original content - // - The caller ensures that the PathExtension is correct - unsafe { SharedReference(self.0.emplace_unchecked(pointer, path_extension, new_span)) } + unsafe { SharedReference(self.0.emplace_unchecked(pointer, path_extension, span)) } } } diff --git a/src/misc/mod.rs b/src/misc/mod.rs index d1d17792..2ce22658 100644 --- a/src/misc/mod.rs +++ b/src/misc/mod.rs @@ -4,18 +4,15 @@ mod errors; mod field_inputs; mod iterators; mod keywords; -mod mut_rc_ref_cell; mod parse_traits; pub(crate) mod string_conversion; pub(crate) use arena::*; -#[allow(unused)] // TODO: Remove unused once integrated pub(crate) use dynamic_references::*; pub(crate) use errors::*; pub(crate) use field_inputs::*; pub(crate) use iterators::*; pub(crate) use keywords::*; -pub(crate) use mut_rc_ref_cell::*; pub(crate) use parse_traits::*; use crate::internal_prelude::*; diff --git a/src/misc/mut_rc_ref_cell.rs b/src/misc/mut_rc_ref_cell.rs deleted file mode 100644 index 8bf147a1..00000000 --- a/src/misc/mut_rc_ref_cell.rs +++ /dev/null @@ -1,461 +0,0 @@ -use crate::internal_prelude::*; -use std::cell::{BorrowError, BorrowMutError}; - -/// A mutable reference to a sub-value `U` inside a [`Rc>`]. -/// 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. - /// - /// SAFETY: This *must* appear before `pointed_at` so that it's dropped first. - ref_mut: RefMut<'static, U>, - pointed_at: Rc>, -} - -impl MutableSubRcRefCell { - pub(crate) fn new(pointed_at: Rc>) -> Result>> { - let ref_mut = match pointed_at.try_borrow_mut() { - Ok(ref_mut) => { - // SAFETY: We must ensure that this lifetime lives as long as the - // reference to pointed_at (i.e. the RefCell). - // This is guaranteed by the fact that the only time we drop the RefCell - // is when we drop the MutRcRefCell, and we ensure that the RefMut is dropped first. - unsafe { - Some(less_buggy_transmute::< - std::cell::RefMut<'_, T>, - std::cell::RefMut<'static, T>, - >(ref_mut)) - } - } - Err(_) => None, - }; - match ref_mut { - Some(ref_mut) => Ok(Self { - ref_mut, - pointed_at, - }), - None => Err(pointed_at), - } - } - - pub(crate) fn new_from_owned(owned: T) -> Self - where - T: Sized, - { - let rc = Rc::new(RefCell::new(owned)); - Self::new(rc).unwrap_or_else(|_| unreachable!("New refcell must be mut borrowable")) - } -} - -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 / 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) - .unwrap_or_else(|_| { - unreachable!("Must be able to create shared after holding mutable") - }) - .map(|_| &*ptr) - } - } - - /// Disables this mutable reference, releasing the borrow on the RefCell. - /// Returns a `DisabledMutableSubRcRefCell` which can be cloned and later re-enabled. - pub(crate) fn disable(self) -> DisabledMutableSubRcRefCell { - let sub_ptr = self.ref_mut.deref() as *const U as *mut U; - // Drop the RefMut to release the borrow - drop(self.ref_mut); - DisabledMutableSubRcRefCell { - pointed_at: self.pointed_at, - sub_ptr, - } - } - - 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, MutableSubRcRefCell)> { - let mut error = None; - let outcome = RefMut::filter_map(self.ref_mut, |inner| match f(inner) { - Ok(value) => Some(value), - Err(e) => { - error = Some(e); - None - } - }); - match outcome { - Ok(ref_mut) => Ok(MutableSubRcRefCell { - ref_mut, - pointed_at: self.pointed_at, - }), - Err(original_ref_mut) => Err(( - error.unwrap(), - MutableSubRcRefCell { - ref_mut: original_ref_mut, - pointed_at: self.pointed_at, - }, - )), - } - } - - 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 { - fn deref_mut(&mut self) -> &mut U { - &mut self.ref_mut - } -} - -impl Deref for MutableSubRcRefCell { - type Target = U; - fn deref(&self) -> &U { - &self.ref_mut - } -} - -/// A disabled mutable reference that can be safely cloned and dropped. -/// -/// This type holds just the `Rc` and a raw pointer to the sub-value, -/// without an active borrow on the RefCell. This means: -/// - Dropping is safe (no borrow count to decrement) -/// - Cloning is safe (just clones the Rc and copies the pointer) -/// - The value cannot be accessed until re-enabled -pub(crate) struct DisabledMutableSubRcRefCell { - pointed_at: Rc>, - sub_ptr: *mut U, -} - -impl Clone for DisabledMutableSubRcRefCell { - fn clone(&self) -> Self { - Self { - pointed_at: Rc::clone(&self.pointed_at), - sub_ptr: self.sub_ptr, - } - } -} - -impl DisabledMutableSubRcRefCell { - /// Re-enables this disabled mutable reference by re-acquiring the borrow. - /// - /// Returns an error if the RefCell is currently borrowed. - pub(crate) fn enable(self) -> Result, BorrowMutError> { - let ref_mut = self.pointed_at.try_borrow_mut()?; - // SAFETY: - // - sub_ptr was derived from a valid &mut U inside the RefCell - // - The Rc is still alive, so the RefCell contents haven't moved - // - We just acquired a mutable borrow, so we have exclusive access - let ref_mut = RefMut::map(ref_mut, |_| unsafe { &mut *self.sub_ptr }); - Ok(MutableSubRcRefCell { - ref_mut: unsafe { less_buggy_transmute::, RefMut<'static, U>>(ref_mut) }, - pointed_at: self.pointed_at, - }) - } - - pub(crate) fn into_shared(self) -> DisabledSharedSubRcRefCell { - DisabledSharedSubRcRefCell { - pointed_at: self.pointed_at, - sub_ptr: self.sub_ptr as *const U, - } - } -} - -/// 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 [`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 - /// to ensure it's dropped first. - /// - /// SAFETY: This *must* appear before `pointed_at` so that it's dropped first. - shared_ref: Ref<'static, U>, - pointed_at: Rc>, -} - -impl SharedSubRcRefCell { - pub(crate) fn new(pointed_at: Referenceable) -> Result> { - let shared_ref = match pointed_at.try_borrow() { - Ok(shared_ref) => { - // SAFETY: We must ensure that this lifetime lives as long as the - // reference to pointed_at (i.e. the RefCell). - // This is guaranteed by the fact that the only time we drop the RefCell - // is when we drop the SharedSubRcRefCell, and we ensure that the Ref is dropped first. - unsafe { - Some(less_buggy_transmute::, Ref<'static, T>>( - shared_ref, - )) - } - } - Err(_) => None, - }; - match shared_ref { - Some(shared_ref) => Ok(Self { - shared_ref, - pointed_at, - }), - None => Err(pointed_at), - } - } - - pub(crate) fn new_from_owned(owned: T) -> Self - where - T: Sized, - { - let rc = Rc::new(RefCell::new(owned)); - Self::new(rc).unwrap_or_else(|_| unreachable!("New refcell must be borrowable")) - } -} - -impl SharedSubRcRefCell { - pub(crate) fn clone(this: &SharedSubRcRefCell) -> Self { - Self { - shared_ref: Ref::clone(&this.shared_ref), - pointed_at: Rc::clone(&this.pointed_at), - } - } - - 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, - } - } - - 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>, - ) -> Result, (E, SharedSubRcRefCell)> { - let mut error = None; - let outcome = Ref::filter_map(self.shared_ref, |inner| match f(inner) { - Ok(value) => Some(value), - Err(e) => { - error = Some(e); - None - } - }); - match outcome { - Ok(shared_ref) => Ok(SharedSubRcRefCell { - shared_ref, - pointed_at: self.pointed_at, - }), - Err(original_shared_ref) => Err(( - error.unwrap(), - SharedSubRcRefCell { - shared_ref: original_shared_ref, - pointed_at: self.pointed_at, - }, - )), - } - } - - pub(crate) fn replace( - self, - f: impl for<'e> FnOnce(&'e U, &mut SharedSubEmplacer<'e, T, U>) -> O, - ) -> O { - 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) - } - - /// Disables this shared reference, releasing the borrow on the RefCell. - /// Returns a `DisabledSharedSubRcRefCell` which can be cloned and later re-enabled. - pub(crate) fn disable(self) -> DisabledSharedSubRcRefCell { - let sub_ptr = self.shared_ref.deref() as *const U; - // Drop the Ref to release the borrow - drop(self.shared_ref); - DisabledSharedSubRcRefCell { - pointed_at: self.pointed_at, - sub_ptr, - } - } -} - -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<'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) }) - } -} - -impl Deref for SharedSubRcRefCell { - type Target = U; - - fn deref(&self) -> &U { - &self.shared_ref - } -} - -/// A disabled shared reference that can be safely cloned and dropped. -/// -/// This type holds just the `Rc` and a raw pointer to the sub-value, -/// without an active borrow on the RefCell. This means: -/// - Dropping is safe (no borrow count to decrement) -/// - Cloning is safe (just clones the Rc and copies the pointer) -/// - The value cannot be accessed until re-enabled -pub(crate) struct DisabledSharedSubRcRefCell { - pointed_at: Rc>, - sub_ptr: *const U, -} - -impl Clone for DisabledSharedSubRcRefCell { - fn clone(&self) -> Self { - Self { - pointed_at: Rc::clone(&self.pointed_at), - sub_ptr: self.sub_ptr, - } - } -} - -impl DisabledSharedSubRcRefCell { - /// Re-enables this disabled shared reference by re-acquiring the borrow. - /// - /// Returns an error if the RefCell is currently mutably borrowed. - pub(crate) fn enable(self) -> Result, BorrowError> { - let shared_ref = self.pointed_at.try_borrow()?; - // SAFETY: - // - sub_ptr was derived from a valid &U inside the RefCell - // - The Rc is still alive, so the RefCell contents haven't moved - // - We just acquired a shared borrow, so the data is valid - let shared_ref = Ref::map(shared_ref, |_| unsafe { &*self.sub_ptr }); - Ok(SharedSubRcRefCell { - shared_ref: unsafe { less_buggy_transmute::, Ref<'static, U>>(shared_ref) }, - pointed_at: self.pointed_at, - }) - } -} - -/// SAFETY: The user must ensure that the two types are transmutable, -/// and in particular are the same size -unsafe fn less_buggy_transmute(t: T) -> U { - // std::mem::transmute::, Ref<'static, T>> for T: ?Sized - // Is fine on latest Rust, but on MSRV only incorrectly flags: - // > error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - // Likely on old versions of Rust, it assumes that Ref is therefore ?Sized (it's not). - // To workaround this, we use a recommendation from https://users.rust-lang.org/t/transmute-doesnt-work-on-generic-types/87272 - // using transmute_copy and manual forgetting - use std::mem::ManuallyDrop; - debug_assert!(std::mem::size_of::() == std::mem::size_of::()); - std::mem::transmute_copy::, U>(&ManuallyDrop::new(t)) -} diff --git a/tests/compilation_failures/expressions/swap_itself.stderr b/tests/compilation_failures/expressions/swap_itself.stderr index 29c94dab..de6412af 100644 --- a/tests/compilation_failures/expressions/swap_itself.stderr +++ b/tests/compilation_failures/expressions/swap_itself.stderr @@ -1,4 +1,8 @@ -error: The variable cannot be modified as it is already being modified +error: Cannot create a mutable reference because it clashes with an existing reference: + This reference : [*active*] &mut a (of type any) + Other reference: [*active*] &mut a (of type any) + Reason : mutation may be observed from the other active reference, which breaks aliasing rules + --> tests/compilation_failures/expressions/swap_itself.rs:6:16 | 6 | a.swap(a);