From 7e801af47fc6db10203527d9329e4c1b1efe2464 Mon Sep 17 00:00:00 2001 From: Ashish Myles Date: Sun, 5 Jan 2025 00:45:31 -0500 Subject: [PATCH] Add an intermediate `DowncastSend` that `DowncastSync` extends. `DowncastSend` and `DowncastSync` provide methods to upcast to `Box` and `Box`, respectively. The addition of a new `DowncastSend` trait that `DowncastSync` depends on is probably backward-compatible since no-one else should be able timplement `DowncastSync` due to object-safety rules. But we need to switch to a new major version anyway. So taking advantage of that here to not worry about backward-compatibility edge cases. Fixes #14. --- src/lib.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 250a3e8..ef61c17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,16 +198,33 @@ impl Downcast for T { fn as_any_mut(&mut self) -> &mut dyn Any { self } } +/// Extends `Downcast` to support `Send` traits that thus support `Box` downcasting as well. +pub trait DowncastSend: Downcast + Send { + /// Convert `Box` (where `Trait: DowncastSend`) to `Box`. + /// `Box` can then be further `downcast` into `Box` where + /// `ConcreteType` implements `Trait`. + fn into_any_send(self: Box) -> Box; +} + +impl DowncastSend for T { + fn into_any_send(self: Box) -> Box { self } +} + #[cfg(feature = "sync")] -/// Extends `Downcast` to support `Sync` traits that thus support `Arc` downcasting as well. -pub trait DowncastSync: Downcast + Send + Sync { - /// Convert `Arc` (where `Trait: Downcast`) to `Arc`. `Arc` can then be +/// Extends `DowncastSend` to support `Sync` traits that thus support `Arc` downcasting as well. +pub trait DowncastSync: DowncastSend + Sync { + /// Convert `Box` (where `Trait: DowncastSync`) to `Box`. + /// `Box` can then be further `downcast` into `Box` where + /// `ConcreteType` implements `Trait`. + fn into_any_sync(self: Box) -> Box; + /// Convert `Arc` (where `Trait: DowncastSync`) to `Arc`. `Arc` can then be /// further `downcast` into `Arc` where `ConcreteType` implements `Trait`. fn into_any_arc(self: Arc) -> Arc; } #[cfg(feature = "sync")] impl DowncastSync for T { + fn into_any_sync(self: Box) -> Box { self } fn into_any_arc(self: Arc) -> Arc { self } } @@ -429,6 +446,20 @@ macro_rules! impl_downcast { #[cfg(all(test, feature = "sync"))] mod test { + /// Substitutes `Downcast` in the body with `DowncastSend`. + macro_rules! subst_downcast_send { + (@impl { $($parsed:tt)* }, {}) => { $($parsed)* }; + (@impl { $($parsed:tt)* }, { Downcast $($rest:tt)* }) => { + subst_downcast_send!(@impl { $($parsed)* DowncastSend }, { $($rest)* }); + }; + (@impl { $($parsed:tt)* }, { $first:tt $($rest:tt)* }) => { + subst_downcast_send!(@impl { $($parsed)* $first }, { $($rest)* }); + }; + ($($body:tt)+) => { + subst_downcast_send!(@impl {}, { $($body)* }); + }; + } + macro_rules! test_mod { ( $test_mod_name:ident, @@ -453,6 +484,7 @@ mod test { sync: { $($sync_def:tt)+ } ) => { mod $test_mod_name { + // Downcast test_mod!( @test $test_mod_name, @@ -462,6 +494,22 @@ mod test { { $($non_sync_def)+ }, []); + // DowncastSend + test_mod!( + @test + $test_mod_name, + test_name: test_send, + trait $base_trait { $($base_impl)* }, + type $base_type, + { subst_downcast_send! { $($non_sync_def)+ } }, + [{ + // Downcast to include Send. + let base: $crate::__alloc::boxed::Box<$base_type> = $crate::__alloc::boxed::Box::new(Foo(42)); + fn impls_send(_a: T) {} + impls_send(base.into_any_send()); + }]); + + // DowncastSync test_mod!( @test $test_mod_name, @@ -470,6 +518,15 @@ mod test { type $base_type, { $($sync_def)+ }, [{ + // Downcast to include Send. + let base: $crate::__alloc::boxed::Box<$base_type> = $crate::__alloc::boxed::Box::new(Foo(42)); + fn impls_send(_a: T) {} + impls_send(base.into_any_send()); + // Downcast to include Send + Sync. + let base: $crate::__alloc::boxed::Box<$base_type> = $crate::__alloc::boxed::Box::new(Foo(42)); + fn impls_send_sync(_a: T) {} + impls_send_sync(base.into_any_sync()); + // Fail to convert Arc into Arc. let arc: $crate::__alloc::sync::Arc<$base_type> = $crate::__alloc::sync::Arc::new(Foo(42)); let res = arc.downcast_arc::(); @@ -494,7 +551,7 @@ mod test { #[test] fn $test_name() { #[allow(unused_imports)] - use super::super::{Downcast, DowncastSync}; + use super::super::{Downcast, DowncastSend, DowncastSync}; // Should work even if standard objects (especially those in the prelude) are // aliased to something else.