77
88use std:: ffi:: OsStr ;
99use std:: intrinsics:: transmute_unchecked;
10+ use std:: marker:: PhantomData ;
1011use std:: mem:: MaybeUninit ;
1112
1213use rustc_ast:: tokenstream:: TokenStream ;
14+ use rustc_data_structures:: sync:: { DynSend , DynSync } ;
1315use rustc_span:: { ErrorGuaranteed , Spanned } ;
1416
1517use crate :: mir:: interpret:: EvalToValTreeResult ;
@@ -18,14 +20,25 @@ use crate::traits::solve;
1820use crate :: ty:: { self , Ty , TyCtxt } ;
1921use crate :: { mir, traits} ;
2022
23+ unsafe extern "C" {
24+ type Hide ;
25+ }
26+
2127/// Internal implementation detail of [`Erased`].
2228#[ derive( Copy , Clone ) ]
2329pub struct ErasedData < Storage : Copy > {
2430 /// We use `MaybeUninit` here to make sure it's legal to store a transmuted
2531 /// value that isn't actually of type `Storage`.
2632 data : MaybeUninit < Storage > ,
33+ /// `Storage` is an erased type, so we use an external type here to opt-out of auto traits
34+ /// as those would be incorrect.
35+ hide : PhantomData < Hide > ,
2736}
2837
38+ // SAFETY: The bounds on `erase_val` ensure the types we erase are `DynSync` and `DynSend`
39+ unsafe impl < Storage : Copy > DynSync for ErasedData < Storage > { }
40+ unsafe impl < Storage : Copy > DynSend for ErasedData < Storage > { }
41+
2942/// Trait for types that can be erased into [`Erased<Self>`].
3043///
3144/// Erasing and unerasing values is performed by [`erase_val`] and [`restore_val`].
@@ -55,13 +68,9 @@ pub type Erased<T: Erasable> = ErasedData<impl Copy>;
5568///
5669/// `Erased<T>` and `Erased<U>` are type-checked as distinct types, but codegen
5770/// can see whether they actually have the same storage type.
58- ///
59- /// FIXME: This might have soundness issues with erasable types that don't
60- /// implement the same auto-traits as `[u8; _]`; see
61- /// <https://github.com/rust-lang/rust/pull/151715#discussion_r2740113250>
6271#[ inline( always) ]
6372#[ define_opaque( Erased ) ]
64- pub fn erase_val < T : Erasable > ( value : T ) -> Erased < T > {
73+ pub fn erase_val < T : Erasable + DynSend + DynSync > ( value : T ) -> Erased < T > {
6574 // Ensure the sizes match
6675 const {
6776 if size_of :: < T > ( ) != size_of :: < T :: Storage > ( ) {
@@ -79,6 +88,7 @@ pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
7988 //
8089 // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
8190 data : unsafe { transmute_unchecked :: < T , MaybeUninit < T :: Storage > > ( value) } ,
91+ hide : PhantomData ,
8292 }
8393}
8494
@@ -89,7 +99,7 @@ pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
8999#[ inline( always) ]
90100#[ define_opaque( Erased ) ]
91101pub fn restore_val < T : Erasable > ( erased_value : Erased < T > ) -> T {
92- let ErasedData { data } : ErasedData < <T as Erasable >:: Storage > = erased_value;
102+ let ErasedData { data, .. } : ErasedData < <T as Erasable >:: Storage > = erased_value;
93103 // See comment in `erase_val` for why we use `transmute_unchecked`.
94104 //
95105 // SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance
0 commit comments