| //! To improve compile times and code size for the compiler itself, query |
| //! values are "erased" in some contexts (e.g. inside in-memory cache types), |
| //! to reduce the number of generic instantiations created during codegen. |
| //! |
| //! See <https://github.com/rust-lang/rust/pull/151715> for some bootstrap-time |
| //! and performance benchmarks. |
| |
| use std::ffi::OsStr; |
| use std::intrinsics::transmute_unchecked; |
| use std::marker::PhantomData; |
| use std::mem::MaybeUninit; |
| |
| use rustc_ast::tokenstream::TokenStream; |
| use rustc_data_structures::steal::Steal; |
| use rustc_data_structures::sync::{DynSend, DynSync}; |
| use rustc_span::{ErrorGuaranteed, Spanned}; |
| |
| use crate::mir::mono::{MonoItem, NormalizationErrorInMono}; |
| use crate::ty::{self, Ty, TyCtxt}; |
| use crate::{mir, thir, traits}; |
| |
| unsafe extern "C" { |
| type NoAutoTraits; |
| } |
| |
| /// Internal implementation detail of [`Erased`]. |
| #[derive(Copy, Clone)] |
| pub struct ErasedData<Storage: Copy> { |
| /// We use `MaybeUninit` here to make sure it's legal to store a transmuted |
| /// value that isn't actually of type `Storage`. |
| data: MaybeUninit<Storage>, |
| /// `Storage` is an erased type, so we use an external type here to opt-out of auto traits |
| /// as those would be incorrect. |
| no_auto_traits: PhantomData<NoAutoTraits>, |
| } |
| |
| // SAFETY: The bounds on `erase_val` ensure the types we erase are `DynSync` and `DynSend` |
| unsafe impl<Storage: Copy> DynSync for ErasedData<Storage> {} |
| unsafe impl<Storage: Copy> DynSend for ErasedData<Storage> {} |
| |
| /// Trait for types that can be erased into [`Erased<Self>`]. |
| /// |
| /// Erasing and unerasing values is performed by [`erase_val`] and [`restore_val`]. |
| /// |
| /// FIXME: This whole trait could potentially be replaced by `T: Copy` and the |
| /// storage type `[u8; size_of::<T>()]` when support for that is more mature. |
| pub trait Erasable: Copy { |
| /// Storage type to used for erased values of this type. |
| /// Should be `[u8; N]`, where N is equal to `size_of::<Self>`. |
| /// |
| /// [`ErasedData`] wraps this storage type in `MaybeUninit` to ensure that |
| /// transmutes to/from erased storage are well-defined. |
| type Storage: Copy; |
| } |
| |
| /// A value of `T` that has been "erased" into some opaque storage type. |
| /// |
| /// This is helpful for reducing the number of concrete instantiations needed |
| /// during codegen when building the compiler. |
| /// |
| /// Using an opaque type alias allows the type checker to enforce that |
| /// `Erased<T>` and `Erased<U>` are still distinct types, while allowing |
| /// monomorphization to see that they might actually use the same storage type. |
| pub type Erased<T: Erasable> = ErasedData<impl Copy>; |
| |
| /// Erases a value of type `T` into `Erased<T>`. |
| /// |
| /// `Erased<T>` and `Erased<U>` are type-checked as distinct types, but codegen |
| /// can see whether they actually have the same storage type. |
| #[inline(always)] |
| #[define_opaque(Erased)] |
| // The `DynSend` and `DynSync` bounds on `T` are used to |
| // justify the safety of the implementations of these traits for `ErasedData`. |
| pub fn erase_val<T: Erasable + DynSend + DynSync>(value: T) -> Erased<T> { |
| // Ensure the sizes match |
| const { |
| if size_of::<T>() != size_of::<T::Storage>() { |
| panic!("size of T must match erased type <T as Erasable>::Storage") |
| } |
| }; |
| |
| ErasedData::<<T as Erasable>::Storage> { |
| // `transmute_unchecked` is needed here because it does not have `transmute`'s size check |
| // (and thus allows to transmute between `T` and `MaybeUninit<T::Storage>`) (we do the size |
| // check ourselves in the `const` block above). |
| // |
| // `transmute_copy` is also commonly used for this (and it would work here since |
| // `Erasable: Copy`), but `transmute_unchecked` better explains the intent. |
| // |
| // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes. |
| data: unsafe { transmute_unchecked::<T, MaybeUninit<T::Storage>>(value) }, |
| no_auto_traits: PhantomData, |
| } |
| } |
| |
| /// Restores an erased value to its real type. |
| /// |
| /// This relies on the fact that `Erased<T>` and `Erased<U>` are type-checked |
| /// as distinct types, even if they use the same storage type. |
| #[inline(always)] |
| #[define_opaque(Erased)] |
| pub fn restore_val<T: Erasable>(erased_value: Erased<T>) -> T { |
| let ErasedData { data, .. }: ErasedData<<T as Erasable>::Storage> = erased_value; |
| // See comment in `erase_val` for why we use `transmute_unchecked`. |
| // |
| // SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance |
| // of `Erased` is to call `erase_val`, so we know that `erased_value.data` is a valid instance |
| // of `T` of the right size. |
| unsafe { transmute_unchecked::<MaybeUninit<T::Storage>, T>(data) } |
| } |
| |
| // FIXME(#151565): Using `T: ?Sized` here should let us remove the separate |
| // impls for fat reference types. |
| impl<T> Erasable for &'_ T { |
| type Storage = [u8; size_of::<&'_ ()>()]; |
| } |
| |
| impl<T> Erasable for &'_ [T] { |
| type Storage = [u8; size_of::<&'_ [()]>()]; |
| } |
| |
| impl<T> Erasable for &'_ ty::List<T> { |
| type Storage = [u8; size_of::<&'_ ty::List<()>>()]; |
| } |
| |
| impl<T> Erasable for &'_ ty::ListWithCachedTypeInfo<T> { |
| type Storage = [u8; size_of::<&'_ ty::ListWithCachedTypeInfo<()>>()]; |
| } |
| |
| impl<T> Erasable for Result<&'_ T, traits::query::NoSolution> { |
| type Storage = [u8; size_of::<Result<&'_ (), traits::query::NoSolution>>()]; |
| } |
| |
| impl<T> Erasable for Result<&'_ T, ErrorGuaranteed> { |
| type Storage = [u8; size_of::<Result<&'_ (), ErrorGuaranteed>>()]; |
| } |
| |
| impl<T> Erasable for Option<&'_ T> { |
| type Storage = [u8; size_of::<Option<&'_ ()>>()]; |
| } |
| |
| impl<T: Erasable> Erasable for ty::EarlyBinder<'_, T> { |
| type Storage = T::Storage; |
| } |
| |
| impl<T0, T1> Erasable for (&'_ T0, &'_ T1) { |
| type Storage = [u8; size_of::<(&'_ (), &'_ ())>()]; |
| } |
| |
| macro_rules! impl_erasable_for_types_with_no_type_params { |
| ($($ty:ty),+ $(,)?) => { |
| $( |
| impl Erasable for $ty { |
| type Storage = [u8; size_of::<$ty>()]; |
| } |
| )* |
| } |
| } |
| |
| // For types with no type parameters the erased storage for `Foo` is |
| // `[u8; size_of::<Foo>()]`. ('_ lifetimes are allowed.) |
| impl_erasable_for_types_with_no_type_params! { |
| // tidy-alphabetical-start |
| (&'_ ty::CrateInherentImpls, Result<(), ErrorGuaranteed>), |
| (), |
| (traits::solve::QueryResult<'_>, &'_ traits::solve::inspect::Probe<TyCtxt<'_>>), |
| Option<&'_ OsStr>, |
| Option<&'_ [rustc_hir::PreciseCapturingArgKind<rustc_span::Symbol, rustc_span::Symbol>]>, |
| Option<(mir::ConstValue, Ty<'_>)>, |
| Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>, |
| Option<rustc_abi::Align>, |
| Option<rustc_ast::expand::allocator::AllocatorKind>, |
| Option<rustc_data_structures::svh::Svh>, |
| Option<rustc_hir::ConstStability>, |
| Option<rustc_hir::CoroutineKind>, |
| Option<rustc_hir::DefaultBodyStability>, |
| Option<rustc_hir::Stability>, |
| Option<rustc_middle::middle::stability::DeprecationEntry>, |
| Option<rustc_middle::ty::AsyncDestructor>, |
| Option<rustc_middle::ty::Destructor>, |
| Option<rustc_middle::ty::IntrinsicDef>, |
| Option<rustc_middle::ty::ScalarInt>, |
| Option<rustc_span::Span>, |
| Option<rustc_span::def_id::CrateNum>, |
| Option<rustc_span::def_id::DefId>, |
| Option<rustc_span::def_id::LocalDefId>, |
| Option<rustc_target::spec::PanicStrategy>, |
| Option<ty::EarlyBinder<'_, Ty<'_>>>, |
| Option<ty::Value<'_>>, |
| Option<usize>, |
| Result<&'_ TokenStream, ()>, |
| Result<&'_ rustc_target::callconv::FnAbi<'_, Ty<'_>>, &'_ ty::layout::FnAbiError<'_>>, |
| Result<&'_ traits::ImplSource<'_, ()>, traits::CodegenObligationError>, |
| Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop>, |
| Result<(&'_ Steal<thir::Thir<'_>>, thir::ExprId), ErrorGuaranteed>, |
| Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>]), NormalizationErrorInMono>, |
| Result<(), ErrorGuaranteed>, |
| Result<Option<ty::EarlyBinder<'_, ty::Const<'_>>>, ErrorGuaranteed>, |
| Result<Option<ty::Instance<'_>>, ErrorGuaranteed>, |
| Result<bool, &ty::layout::LayoutError<'_>>, |
| Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled>, |
| Result<mir::ConstValue, mir::interpret::ErrorHandled>, |
| Result<rustc_abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::LayoutError<'_>>, |
| Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>, |
| Result<rustc_middle::ty::adjustment::CoerceUnsizedInfo, ErrorGuaranteed>, |
| Result<ty::GenericArg<'_>, traits::query::NoSolution>, |
| Ty<'_>, |
| bool, |
| rustc_data_structures::svh::Svh, |
| rustc_hir::Constness, |
| rustc_hir::Defaultness, |
| rustc_hir::HirId, |
| rustc_hir::MaybeOwner<'_>, |
| rustc_hir::OpaqueTyOrigin<rustc_hir::def_id::DefId>, |
| rustc_hir::def::DefKind, |
| rustc_hir::def_id::DefId, |
| rustc_middle::middle::codegen_fn_attrs::SanitizerFnAttrs, |
| rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault, |
| rustc_middle::mir::ConstQualifs, |
| rustc_middle::mir::ConstValue, |
| rustc_middle::mir::interpret::AllocId, |
| rustc_middle::mir::interpret::EvalStaticInitializerRawResult<'_>, |
| rustc_middle::mir::interpret::EvalToValTreeResult<'_>, |
| rustc_middle::mir::mono::MonoItemPartitions<'_>, |
| rustc_middle::traits::query::MethodAutoderefStepsResult<'_>, |
| rustc_middle::ty::AdtDef<'_>, |
| rustc_middle::ty::AnonConstKind, |
| rustc_middle::ty::AssocItem, |
| rustc_middle::ty::Asyncness, |
| rustc_middle::ty::Binder<'_, ty::CoroutineWitnessTypes<TyCtxt<'_>>>, |
| rustc_middle::ty::Binder<'_, ty::FnSig<'_>>, |
| rustc_middle::ty::ClosureTypeInfo<'_>, |
| rustc_middle::ty::Const<'_>, |
| rustc_middle::ty::ConstConditions<'_>, |
| rustc_middle::ty::GenericPredicates<'_>, |
| rustc_middle::ty::ImplTraitHeader<'_>, |
| rustc_middle::ty::ParamEnv<'_>, |
| rustc_middle::ty::SymbolName<'_>, |
| rustc_middle::ty::TypingEnv<'_>, |
| rustc_middle::ty::Visibility<rustc_span::def_id::DefId>, |
| rustc_middle::ty::inhabitedness::InhabitedPredicate<'_>, |
| rustc_session::Limits, |
| rustc_session::config::OptLevel, |
| rustc_session::config::SymbolManglingVersion, |
| rustc_session::cstore::CrateDepKind, |
| rustc_span::ExpnId, |
| rustc_span::Span, |
| rustc_span::Symbol, |
| rustc_target::spec::PanicStrategy, |
| usize, |
| // tidy-alphabetical-end |
| } |