Skip to main content

rustc_middle/query/
erase.rs

1//! To improve compile times and code size for the compiler itself, query
2//! values are "erased" in some contexts (e.g. inside in-memory cache types),
3//! to reduce the number of generic instantiations created during codegen.
4//!
5//! See <https://github.com/rust-lang/rust/pull/151715> for some bootstrap-time
6//! and performance benchmarks.
7
8use std::ffi::OsStr;
9use std::intrinsics::transmute_unchecked;
10use std::marker::PhantomData;
11use std::mem::MaybeUninit;
12
13use rustc_ast::tokenstream::TokenStream;
14use rustc_data_structures::steal::Steal;
15use rustc_data_structures::sync::{DynSend, DynSync};
16use rustc_span::def_id::ModId;
17use rustc_span::{ErrorGuaranteed, Spanned};
18
19use crate::mono::{MonoItem, NormalizationErrorInMono};
20use crate::ty::{self, Ty, TyCtxt};
21use crate::{mir, thir, traits};
22
23unsafe extern "C" {
24    type NoAutoTraits;
25}
26
27/// Internal implementation detail of [`Erased`].
28#[derive(#[automatically_derived]
impl<Storage: ::core::marker::Copy + Copy> ::core::marker::Copy for
    ErasedData<Storage> {
}Copy, #[automatically_derived]
impl<Storage: ::core::clone::Clone + Copy> ::core::clone::Clone for
    ErasedData<Storage> {
    #[inline]
    fn clone(&self) -> ErasedData<Storage> {
        ErasedData {
            data: ::core::clone::Clone::clone(&self.data),
            no_auto_traits: ::core::clone::Clone::clone(&self.no_auto_traits),
        }
    }
}Clone)]
29pub struct ErasedData<Storage: Copy> {
30    /// We use `MaybeUninit` here to make sure it's legal to store a transmuted
31    /// value that isn't actually of type `Storage`.
32    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>,
36}
37
38// SAFETY: The bounds on `erase_val` ensure the types we erase are `DynSync` and `DynSend`
39unsafe impl<Storage: Copy> DynSync for ErasedData<Storage> {}
40unsafe impl<Storage: Copy> DynSend for ErasedData<Storage> {}
41
42/// Trait for types that can be erased into [`Erased<Self>`].
43///
44/// Erasing and unerasing values is performed by [`erase_val`] and [`restore_val`].
45///
46/// FIXME: This whole trait could potentially be replaced by `T: Copy` and the
47/// storage type `[u8; size_of::<T>()]` when support for that is more mature.
48pub trait Erasable: Copy {
49    /// Storage type to used for erased values of this type.
50    /// Should be `[u8; N]`, where N is equal to `size_of::<Self>`.
51    ///
52    /// [`ErasedData`] wraps this storage type in `MaybeUninit` to ensure that
53    /// transmutes to/from erased storage are well-defined.
54    type Storage: Copy;
55}
56
57/// A value of `T` that has been "erased" into some opaque storage type.
58///
59/// This is helpful for reducing the number of concrete instantiations needed
60/// during codegen when building the compiler.
61///
62/// Using an opaque type alias allows the type checker to enforce that
63/// `Erased<T>` and `Erased<U>` are still distinct types, while allowing
64/// monomorphization to see that they might actually use the same storage type.
65pub type Erased<T: Erasable> = ErasedData<impl Copy>;
66
67/// Erases a value of type `T` into `Erased<T>`.
68///
69/// `Erased<T>` and `Erased<U>` are type-checked as distinct types, but codegen
70/// can see whether they actually have the same storage type.
71#[inline(always)]
72#[define_opaque(Erased)]
73// The `DynSend` and `DynSync` bounds on `T` are used to
74// justify the safety of the implementations of these traits for `ErasedData`.
75pub fn erase_val<T: Erasable + DynSend + DynSync>(value: T) -> Erased<T> {
76    // Ensure the sizes match
77    const {
78        if size_of::<T>() != size_of::<T::Storage>() {
79            {
    ::core::panicking::panic_fmt(format_args!("size of T must match erased type <T as Erasable>::Storage"));
}panic!("size of T must match erased type <T as Erasable>::Storage")
80        }
81    };
82
83    ErasedData::<<T as Erasable>::Storage> {
84        // `transmute_unchecked` is needed here because it does not have `transmute`'s size check
85        // (and thus allows to transmute between `T` and `MaybeUninit<T::Storage>`) (we do the size
86        // check ourselves in the `const` block above).
87        //
88        // `transmute_copy` is also commonly used for this (and it would work here since
89        // `Erasable: Copy`), but `transmute_unchecked` better explains the intent.
90        //
91        // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
92        data: unsafe { transmute_unchecked::<T, MaybeUninit<T::Storage>>(value) },
93        no_auto_traits: PhantomData,
94    }
95}
96
97/// Restores an erased value to its real type.
98///
99/// This relies on the fact that `Erased<T>` and `Erased<U>` are type-checked
100/// as distinct types, even if they use the same storage type.
101#[inline(always)]
102#[define_opaque(Erased)]
103pub fn restore_val<T: Erasable>(erased_value: Erased<T>) -> T {
104    let ErasedData { data, .. }: ErasedData<<T as Erasable>::Storage> = erased_value;
105    // See comment in `erase_val` for why we use `transmute_unchecked`.
106    //
107    // SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance
108    // of `Erased` is to call `erase_val`, so we know that `erased_value.data` is a valid instance
109    // of `T` of the right size.
110    unsafe { transmute_unchecked::<MaybeUninit<T::Storage>, T>(data) }
111}
112
113impl<T> Erasable for &'_ T {
114    type Storage = [u8; size_of::<&'_ ()>()];
115}
116
117impl<T> Erasable for &'_ [T] {
118    type Storage = [u8; size_of::<&'_ [()]>()];
119}
120
121// Note: this impl does not overlap with the impl for `&'_ T` above because `RawList` is unsized
122// and does not satisfy the implicit `T: Sized` bound.
123//
124// Furthermore, even if that implicit bound was removed (by adding `T: ?Sized`) this impl still
125// wouldn't overlap because `?Sized` is equivalent to `MetaSized` and `RawList` does not satisfy
126// `MetaSized` because it contains an extern type.
127impl<H, T> Erasable for &'_ ty::RawList<H, T> {
128    type Storage = [u8; size_of::<&'_ ty::RawList<(), ()>>()];
129}
130
131impl<T> Erasable for Result<&'_ T, traits::query::NoSolution> {
132    type Storage = [u8; size_of::<Result<&'_ (), traits::query::NoSolution>>()];
133}
134
135impl<T> Erasable for Result<&'_ T, ErrorGuaranteed> {
136    type Storage = [u8; size_of::<Result<&'_ (), ErrorGuaranteed>>()];
137}
138
139impl<T> Erasable for Option<&'_ T> {
140    type Storage = [u8; size_of::<Option<&'_ ()>>()];
141}
142
143impl<T: Erasable> Erasable for ty::EarlyBinder<'_, T> {
144    type Storage = T::Storage;
145}
146
147impl<T0, T1> Erasable for (&'_ T0, &'_ T1) {
148    type Storage = [u8; size_of::<(&'_ (), &'_ ())>()];
149}
150
151impl<T0, T1, T2> Erasable for (&'_ T0, &'_ T1, &'_ T2) {
152    type Storage = [u8; size_of::<(&'_ (), &'_ (), &'_ ())>()];
153}
154
155impl<T0, T1> Erasable for (&'_ [T0], &'_ [T1]) {
156    type Storage = [u8; size_of::<(&'_ [()], &'_ [()])>()];
157}
158
159macro_rules! impl_erasable_for_types_with_no_type_params {
160    ($($ty:ty),+ $(,)?) => {
161        $(
162            impl Erasable for $ty {
163                type Storage = [u8; size_of::<$ty>()];
164            }
165        )*
166    }
167}
168
169// For types with no type parameters the erased storage for `Foo` is
170// `[u8; size_of::<Foo>()]`. ('_ lifetimes are allowed.)
171impl Erasable for usize {
    type Storage = [u8; size_of::<usize>()];
}impl_erasable_for_types_with_no_type_params! {
172    // tidy-alphabetical-start
173    (&'_ ty::CrateInherentImpls, Result<(), ErrorGuaranteed>),
174    (),
175    (traits::solve::QueryResult<'_>, &'_ traits::solve::inspect::Probe<TyCtxt<'_>>),
176    Option<&'_ OsStr>,
177    Option<&'_ [rustc_hir::PreciseCapturingArgKind<rustc_span::Symbol, rustc_span::Symbol>]>,
178    Option<(mir::ConstValue, Ty<'_>)>,
179    Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>,
180    Option<rustc_abi::Align>,
181    Option<rustc_ast::expand::allocator::AllocatorKind>,
182    Option<rustc_data_structures::svh::Svh>,
183    Option<rustc_hir::ConstStability>,
184    Option<rustc_hir::CoroutineKind>,
185    Option<rustc_hir::DefaultBodyStability>,
186    Option<rustc_hir::Stability>,
187    Option<rustc_middle::middle::stability::DeprecationEntry>,
188    Option<rustc_middle::ty::AsyncDestructor>,
189    Option<rustc_middle::ty::Destructor>,
190    Option<rustc_middle::ty::IntrinsicDef>,
191    Option<rustc_middle::ty::ScalarInt>,
192    Option<rustc_span::Span>,
193    Option<rustc_span::def_id::CrateNum>,
194    Option<rustc_span::def_id::DefId>,
195    Option<rustc_span::def_id::LocalDefId>,
196    Option<rustc_target::spec::PanicStrategy>,
197    Option<ty::EarlyBinder<'_, Ty<'_>>>,
198    Option<ty::Value<'_>>,
199    Option<usize>,
200    Result<&'_ TokenStream, ()>,
201    Result<&'_ rustc_target::callconv::FnAbi<'_, Ty<'_>>, &'_ ty::layout::FnAbiError<'_>>,
202    Result<&'_ traits::ImplSource<'_, ()>, traits::CodegenObligationError>,
203    Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop>,
204    Result<(&'_ Steal<thir::Thir<'_>>, thir::ExprId), ErrorGuaranteed>,
205    Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>]), NormalizationErrorInMono>,
206    Result<(), ErrorGuaranteed>,
207    Result<Option<ty::EarlyBinder<'_, ty::Const<'_>>>, ErrorGuaranteed>,
208    Result<Option<ty::Instance<'_>>, ErrorGuaranteed>,
209    Result<bool, &ty::layout::LayoutError<'_>>,
210    Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled>,
211    Result<mir::ConstValue, mir::interpret::ErrorHandled>,
212    Result<rustc_abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::LayoutError<'_>>,
213    Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>,
214    Result<rustc_middle::ty::adjustment::CoerceUnsizedInfo, ErrorGuaranteed>,
215    Result<ty::GenericArg<'_>, traits::query::NoSolution>,
216    Ty<'_>,
217    bool,
218    rustc_data_structures::svh::Svh,
219    rustc_hir::Constness,
220    rustc_hir::Defaultness,
221    rustc_hir::HirId,
222    rustc_hir::MaybeOwner<'_>,
223    rustc_hir::OpaqueTyOrigin<rustc_hir::def_id::DefId>,
224    rustc_hir::def::DefKind,
225    rustc_hir::def_id::DefId,
226    rustc_middle::hir::ProjectedMaybeOwner<'_>,
227    rustc_middle::middle::codegen_fn_attrs::SanitizerFnAttrs,
228    rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault,
229    rustc_middle::mir::ConstQualifs,
230    rustc_middle::mir::ConstValue,
231    rustc_middle::mir::interpret::AllocId,
232    rustc_middle::mir::interpret::EvalStaticInitializerRawResult<'_>,
233    rustc_middle::mir::interpret::EvalToValTreeResult<'_>,
234    rustc_middle::mono::MonoItemPartitions<'_>,
235    rustc_middle::traits::query::MethodAutoderefStepsResult<'_>,
236    rustc_middle::ty::AdtDef<'_>,
237    rustc_middle::ty::AnonConstKind,
238    rustc_middle::ty::AssocItem,
239    rustc_middle::ty::Asyncness,
240    rustc_middle::ty::Binder<'_, ty::CoroutineWitnessTypes<TyCtxt<'_>>>,
241    rustc_middle::ty::Binder<'_, ty::FnSig<'_>>,
242    rustc_middle::ty::ClosureTypeInfo<'_>,
243    rustc_middle::ty::Const<'_>,
244    rustc_middle::ty::ConstConditions<'_>,
245    rustc_middle::ty::GenericClauses<'_>,
246    rustc_middle::ty::ImplTraitHeader<'_>,
247    rustc_middle::ty::ParamEnv<'_>,
248    rustc_middle::ty::SymbolName<'_>,
249    rustc_middle::ty::TypingEnv<'_>,
250    rustc_middle::ty::Visibility<ModId>,
251    rustc_middle::ty::inhabitedness::InhabitedPredicate<'_>,
252    rustc_session::Limits,
253    rustc_session::config::OptLevel,
254    rustc_session::config::SymbolManglingVersion,
255    rustc_session::cstore::CrateDepKind,
256    rustc_span::ExpnId,
257    rustc_span::Span,
258    rustc_span::Symbol,
259    rustc_target::spec::PanicStrategy,
260    usize,
261    // tidy-alphabetical-end
262}