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 ;
1314use rustc_data_structures:: steal:: Steal ;
15+ use rustc_data_structures:: sync:: { DynSend , DynSync } ;
1416use rustc_span:: { ErrorGuaranteed , Spanned } ;
1517
1618use crate :: mir:: mono:: { MonoItem , NormalizationErrorInMono } ;
1719use crate :: ty:: { self , Ty , TyCtxt } ;
1820use crate :: { mir, thir, traits} ;
1921
22+ unsafe extern "C" {
23+ type NoAutoTraits ;
24+ }
25+
2026/// Internal implementation detail of [`Erased`].
2127#[ derive( Copy , Clone ) ]
2228pub struct ErasedData < Storage : Copy > {
2329 /// We use `MaybeUninit` here to make sure it's legal to store a transmuted
2430 /// value that isn't actually of type `Storage`.
2531 data : MaybeUninit < Storage > ,
32+ /// `Storage` is an erased type, so we use an external type here to opt-out of auto traits
33+ /// as those would be incorrect.
34+ no_auto_traits : PhantomData < NoAutoTraits > ,
2635}
2736
37+ // SAFETY: The bounds on `erase_val` ensure the types we erase are `DynSync` and `DynSend`
38+ unsafe impl < Storage : Copy > DynSync for ErasedData < Storage > { }
39+ unsafe impl < Storage : Copy > DynSend for ErasedData < Storage > { }
40+
2841/// Trait for types that can be erased into [`Erased<Self>`].
2942///
3043/// Erasing and unerasing values is performed by [`erase_val`] and [`restore_val`].
@@ -54,13 +67,11 @@ pub type Erased<T: Erasable> = ErasedData<impl Copy>;
5467///
5568/// `Erased<T>` and `Erased<U>` are type-checked as distinct types, but codegen
5669/// can see whether they actually have the same storage type.
57- ///
58- /// FIXME: This might have soundness issues with erasable types that don't
59- /// implement the same auto-traits as `[u8; _]`; see
60- /// <https://github.com/rust-lang/rust/pull/151715#discussion_r2740113250>
6170#[ inline( always) ]
6271#[ define_opaque( Erased ) ]
63- pub fn erase_val < T : Erasable > ( value : T ) -> Erased < T > {
72+ // The `DynSend` and `DynSync` bounds on `T` are used to
73+ // justify the safety of the implementations of these traits for `ErasedData`.
74+ pub fn erase_val < T : Erasable + DynSend + DynSync > ( value : T ) -> Erased < T > {
6475 // Ensure the sizes match
6576 const {
6677 if size_of :: < T > ( ) != size_of :: < T :: Storage > ( ) {
@@ -78,6 +89,7 @@ pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
7889 //
7990 // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
8091 data : unsafe { transmute_unchecked :: < T , MaybeUninit < T :: Storage > > ( value) } ,
92+ no_auto_traits : PhantomData ,
8193 }
8294}
8395
@@ -88,7 +100,7 @@ pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
88100#[ inline( always) ]
89101#[ define_opaque( Erased ) ]
90102pub fn restore_val < T : Erasable > ( erased_value : Erased < T > ) -> T {
91- let ErasedData { data } : ErasedData < <T as Erasable >:: Storage > = erased_value;
103+ let ErasedData { data, .. } : ErasedData < <T as Erasable >:: Storage > = erased_value;
92104 // See comment in `erase_val` for why we use `transmute_unchecked`.
93105 //
94106 // SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance
0 commit comments