From 189ce8749b35167077a495e830345afd148910ca Mon Sep 17 00:00:00 2001 From: Qkessler Date: Fri, 8 Dec 2023 12:17:36 +0100 Subject: [PATCH] Make Trace be cross-crate compatible 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`, 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`. All of this and more discussions are part of this issue: https://github.com/CeleritasCelery/rune/issues/42 --- crates/rune-macros/trace.rs | 14 ++++++-------- src/core/gc/root.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/crates/rune-macros/trace.rs b/crates/rune-macros/trace.rs index 8d4adbc9..649b2ed6 100644 --- a/crates/rune-macros/trace.rs +++ b/crates/rune-macros/trace.rs @@ -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::() } - } - } - 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::() } + fn rooted_deref(rooted: &crate::core::gc::Rt) -> &Self::Target { + unsafe { &*(rooted as *const crate::core::gc::Rt).cast::() } } + + fn rooted_derefmut(rooted: &mut crate::core::gc::Rt) -> &mut Self::Target { + unsafe { &mut *(rooted as *mut crate::core::gc::Rt).cast::() } } } } diff --git a/src/core/gc/root.rs b/src/core/gc/root.rs index 154042fc..3a980e10 100644 --- a/src/core/gc/root.rs +++ b/src/core/gc/root.rs @@ -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).cast::() } +/// ``` +/// +/// 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\](`self::Rt`). +pub trait RootedDeref { + type Target; + fn rooted_deref(rooted: &Rt) -> &Self::Target; + fn rooted_derefmut(rooted: &mut Rt) -> &mut Self::Target; +} + +impl Deref for Rt { + type Target = ::Target; + fn deref(&self) -> &Self::Target { + RootedDeref::rooted_deref(self) + } +} + +impl DerefMut for Rt { + 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