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 NoAutoTraits ;
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+ no_auto_traits : PhantomData < NoAutoTraits > ,
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,11 @@ 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+ // The `DynSend` and `DynSync` bounds on `T` are used to
74+ // justify the safety of the implementations of these traits for `ErasedData`.
75+ pub fn erase_val < T : Erasable + DynSend + DynSync > ( value : T ) -> Erased < T > {
6576 // Ensure the sizes match
6677 const {
6778 if size_of :: < T > ( ) != size_of :: < T :: Storage > ( ) {
@@ -79,6 +90,7 @@ pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
7990 //
8091 // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
8192 data : unsafe { transmute_unchecked :: < T , MaybeUninit < T :: Storage > > ( value) } ,
93+ no_auto_traits : PhantomData ,
8294 }
8395}
8496
@@ -89,7 +101,7 @@ pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
89101#[ inline( always) ]
90102#[ define_opaque( Erased ) ]
91103pub fn restore_val < T : Erasable > ( erased_value : Erased < T > ) -> T {
92- let ErasedData { data } : ErasedData < <T as Erasable >:: Storage > = erased_value;
104+ let ErasedData { data, .. } : ErasedData < <T as Erasable >:: Storage > = erased_value;
93105 // See comment in `erase_val` for why we use `transmute_unchecked`.
94106 //
95107 // SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance
0 commit comments