Skip to content

Commit

Permalink
Make Trace be cross-crate compatible
Browse files Browse the repository at this point in the history
This is a key and PRable part of the effort of refactoring the core out
of the main crate to an auxiliar crate, imported by all the other
levels: rune_core.

To avoid the orphan rule (See RFC 1023 - rebalancing coherence) we can't
implement foreign traits for foreign types. A plain example is us
implementing `Deref` and `DerefMut` for the "future" foreign type
`Rt`. As `Rt` (with the core refactor) would now be foreign, we fall
into the orphan rule.

We overcome that limitation by introducing a trait that would act as the
foreign trait, but the key difference is that we are now implementing it
on the actual type `T`, not the rooted type `Rt<T>`, as part of the
`Trace` macro.

When types now derive the `Trace` macro, we implement `RootedDeref` and
whenever the rooted types need to access `deref` or `deref_mut`, they
are able to do so, as we also implement blanket implementations below
it, for all rooted types `Rt<T>`.

All of this and more discussions are part of this issue:
CeleritasCelery#42
  • Loading branch information
Qkessler committed Dec 8, 2023
1 parent 46fd144 commit 189ce87
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 8 deletions.
14 changes: 6 additions & 8 deletions crates/rune-macros/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,14 @@ pub(crate) fn expand(orig: &syn::DeriveInput) -> TokenStream {
quote! {
#derive

impl std::ops::Deref for #rt<#orig_name #static_generics> {
impl crate::core::gc::RootedDeref for #orig_name #static_generics {
type Target = #rooted_name #static_generics;
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const Self).cast::<Self::Target>() }
}
}

impl std::ops::DerefMut for #rt<#orig_name #static_generics> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self as *mut Self).cast::<Self::Target>() }
fn rooted_deref(rooted: &crate::core::gc::Rt<Self>) -> &Self::Target {
unsafe { &*(rooted as *const crate::core::gc::Rt<Self>).cast::<Self::Target>() } }

fn rooted_derefmut(rooted: &mut crate::core::gc::Rt<Self>) -> &mut Self::Target {
unsafe { &mut *(rooted as *mut crate::core::gc::Rt<Self>).cast::<Self::Target>() }
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/core/gc/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,36 @@ macro_rules! root {
};
}

/// Trait created to overpass the orphan rule when deriving the
/// [Trace](`rune_macros::Trace`) derive macro. The derive
/// macro contains a blanket `Deref` (and `DerefMut`) like this:
///
/// ```ignore
/// unsafe { &*(rt as *const Rt<Self>).cast::<Self::Target>() }
/// ```
///
/// By creating a trait that the functions defined in the main crate
/// can define, we avoid the orphan rule by implementing `Deref`
/// on the rooted version of the types: [Rt\<T\>](`self::Rt`).
pub trait RootedDeref {
type Target;
fn rooted_deref(rooted: &Rt<Self>) -> &Self::Target;
fn rooted_derefmut(rooted: &mut Rt<Self>) -> &mut Self::Target;
}

impl<T: RootedDeref> Deref for Rt<T> {
type Target = <T as RootedDeref>::Target;
fn deref(&self) -> &Self::Target {
RootedDeref::rooted_deref(self)
}
}

impl<T: RootedDeref> DerefMut for Rt<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
RootedDeref::rooted_derefmut(self)
}
}

/// A Rooted type. If a type is wrapped in Rt, it is known to be rooted and hold
/// items past garbage collection. This type is never used as an owned type,
/// only a reference. This ensures that underlying data does not move. In order
Expand Down

0 comments on commit 189ce87

Please sign in to comment.