diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 53c5e28c0be2..7d820403ccb7 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -1007,18 +1007,23 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] pub const fn try_as_dyn< - T: Any + 'static, + T: Any + ?Sized + 'static, U: ptr::Pointee> + ?Sized + 'static, >( t: &T, ) -> Option<&U> { + // For unsized `T`, `trait_info_of` always returns `None` (vtable lookup is + // only supported for sized types). The function therefore unconditionally + // returns `None` in that case. let vtable: Option> = const { TypeId::of::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; match vtable { Some(dyn_metadata) => { - let pointer = ptr::from_raw_parts(t, dyn_metadata); + let pointer = ptr::from_raw_parts(t as *const T as *const (), dyn_metadata); // SAFETY: `t` is a reference to a type, so we know it is valid. // `dyn_metadata` is a vtable for T, implementing the trait of `U`. + // `T` is sized here because `trait_info_of` only returns `Some` for sized types, + // so the thin data pointer fully describes the value. Some(unsafe { &*pointer }) } None => None, @@ -1061,18 +1066,23 @@ pub const fn try_as_dyn< #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] pub const fn try_as_dyn_mut< - T: Any + 'static, + T: Any + ?Sized + 'static, U: ptr::Pointee> + ?Sized + 'static, >( t: &mut T, ) -> Option<&mut U> { + // For unsized `T`, `trait_info_of` always returns `None` (vtable lookup is + // only supported for sized types). The function therefore unconditionally + // returns `None` in that case. let vtable: Option> = const { TypeId::of::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; match vtable { Some(dyn_metadata) => { - let pointer = ptr::from_raw_parts_mut(t, dyn_metadata); + let pointer = ptr::from_raw_parts_mut(t as *mut T as *mut (), dyn_metadata); // SAFETY: `t` is a reference to a type, so we know it is valid. // `dyn_metadata` is a vtable for T, implementing the trait of `U`. + // `T` is sized here because `trait_info_of` only returns `Some` for sized types, + // so the thin data pointer fully describes the value. Some(unsafe { &mut *pointer }) } None => None, diff --git a/tests/ui/any/try_as_dyn_unsized.rs b/tests/ui/any/try_as_dyn_unsized.rs new file mode 100644 index 000000000000..83bf1e2863c4 --- /dev/null +++ b/tests/ui/any/try_as_dyn_unsized.rs @@ -0,0 +1,34 @@ +//@ run-pass +#![feature(try_as_dyn)] + +use std::fmt::Debug; + +// Generic over `?Sized` T: relies on the relaxed bound on `try_as_dyn`. +fn try_debug(t: &T) -> Option { + std::any::try_as_dyn::(t).map(|d| format!("{d:?}")) +} + +fn try_debug_mut(t: &mut T) -> Option { + std::any::try_as_dyn_mut::(t).map(|d| format!("{d:?}")) +} + +fn main() { + // Sized case still works through a `?Sized` generic context. + let x: i32 = 7; + assert_eq!(try_debug(&x).as_deref(), Some("7")); + + let mut y: i32 = 8; + assert_eq!(try_debug_mut(&mut y).as_deref(), Some("8")); + + // Unsized `T` always returns `None`, even though `str: Debug` and + // `[T]: Debug` hold — vtable lookup for unsized impl types is not + // currently supported by `TypeId::trait_info_of`. + let s: &str = "hello"; + assert!(try_debug::(s).is_none()); + + let slice: &[i32] = &[1, 2, 3]; + assert!(try_debug::<[i32]>(slice).is_none()); + + let dyn_any: &dyn std::any::Any = &0i32; + assert!(try_debug::(dyn_any).is_none()); +}