diff --git a/crates/turbo-tasks-memory/tests/generics.rs b/crates/turbo-tasks-memory/tests/generics.rs index 9df0a75b0f537..8fd43af719794 100644 --- a/crates/turbo-tasks-memory/tests/generics.rs +++ b/crates/turbo-tasks-memory/tests/generics.rs @@ -147,7 +147,6 @@ async fn test_changing_generic() { // Test that we can convert a `Vc` to a `ReadRef`, and then back to a `Vc`. #[tokio::test] -#[ignore = "TODO: This panics! There's a casting bug in the generics implementation."] async fn test_read_ref_round_trip() { run(®ISTRATION, async move { let c: Vc>> = Vc::cell(Some(Vc::cell(1))); diff --git a/crates/turbo-tasks/src/read_ref.rs b/crates/turbo-tasks/src/read_ref.rs index df4b6e4f3df2c..847532639b2e8 100644 --- a/crates/turbo-tasks/src/read_ref.rs +++ b/crates/turbo-tasks/src/read_ref.rs @@ -12,6 +12,7 @@ use crate::{ debug::{ValueDebugFormat, ValueDebugFormatString}, macro_helpers::find_cell_by_type, trace::{TraceRawVcs, TraceRawVcsContext}, + triomphe_utils::unchecked_sidecast_triomphe_arc, SharedReference, Vc, VcRead, VcValueType, }; @@ -242,8 +243,13 @@ where /// Returns a new cell that points to the same value as the given /// reference. pub fn cell(read_ref: ReadRef) -> Vc { - let local_cell = find_cell_by_type(T::get_value_type_id()); - local_cell.update_shared_reference(SharedReference::new(read_ref.0)); + let type_id = T::get_value_type_id(); + let local_cell = find_cell_by_type(type_id); + // SAFETY: `T` and `T::Read::Repr` must have equivalent memory representations, + // guaranteed by the unsafe implementation of `VcValueType`. + local_cell.update_shared_reference(SharedReference::new(unsafe { + unchecked_sidecast_triomphe_arc::>::Repr>(read_ref.0) + })); Vc { node: local_cell.into(), _t: PhantomData, diff --git a/crates/turbo-tasks/src/triomphe_utils.rs b/crates/turbo-tasks/src/triomphe_utils.rs index 750a1113b2644..f186e26c793d7 100644 --- a/crates/turbo-tasks/src/triomphe_utils.rs +++ b/crates/turbo-tasks/src/triomphe_utils.rs @@ -5,26 +5,45 @@ use unsize::Coercion; /// Attempt to downcast a [`triomphe::Arc`][`triomphe::Arc`] to a concrete type. /// +/// Checks that the downcast is safe using [`Any::is`]. +/// /// Ported from [`std::sync::Arc::downcast`] to [`triomphe::Arc`]. pub fn downcast_triomphe_arc( this: triomphe::Arc, ) -> Result, triomphe::Arc> { if (*this).is::() { - unsafe { - // Get the pointer to the offset (*const T) inside of the ArcInner. - let ptr = triomphe::Arc::into_raw(this); - // SAFETY: The negative offset from the data (ptr) in an Arc to the start of the - // data structure is fixed regardless of type `T`. - // - // SAFETY: Casting from a fat pointer to a thin pointer is safe, as long as the - // types are compatible (they are). - Ok(triomphe::Arc::from_raw(ptr.cast())) - } + unsafe { Ok(unchecked_sidecast_triomphe_arc(this)) } } else { Err(this) } } +/// Transmutes the contents of `Arc` to `Arc`. Updates the `Arc`'s fat +/// pointer metadata. +/// +/// Unlike [`downcast_triomphe_arc`] this make no checks the transmute is safe. +/// +/// # Safety +/// +/// It must be [safe to transmute][transmutes] from `T` to `U`. +/// +/// [transmutes]: https://doc.rust-lang.org/nomicon/transmutes.html +pub unsafe fn unchecked_sidecast_triomphe_arc(this: triomphe::Arc) -> triomphe::Arc +where + T: ?Sized, +{ + unsafe { + // Get the pointer to the offset (*const T) inside of the ArcInner. + let ptr = triomphe::Arc::into_raw(this); + // SAFETY: The negative offset from the data (ptr) in an Arc to the start of the + // data structure is fixed regardless of type `T`. + // + // SAFETY: Casting from a fat pointer to a thin pointer is safe, as long as the + // types are compatible (they are). + triomphe::Arc::from_raw(ptr.cast()) + } +} + /// [`Coercion::to_any`] except that it coerces to `dyn Any + Send + Sync` as /// opposed to `dyn Any`. pub fn coerce_to_any_send_sync() -> Coercion {