safely transmute<&List<Ty<'tcx>>, &List<GenericArg<'tcx>>>
This commit is contained in:
parent
45e2c2881d
commit
a9c1ab82f5
3 changed files with 89 additions and 6 deletions
|
@ -101,7 +101,6 @@ pub struct CtxtInterners<'tcx> {
|
|||
// Specifically use a speedy hash algorithm for these hash sets, since
|
||||
// they're accessed quite often.
|
||||
type_: InternedSet<'tcx, TyS<'tcx>>,
|
||||
type_list: InternedSet<'tcx, List<Ty<'tcx>>>,
|
||||
substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
|
||||
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
|
||||
region: InternedSet<'tcx, RegionKind>,
|
||||
|
@ -129,7 +128,6 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
CtxtInterners {
|
||||
arena,
|
||||
type_: Default::default(),
|
||||
type_list: Default::default(),
|
||||
substs: Default::default(),
|
||||
region: Default::default(),
|
||||
poly_existential_predicates: Default::default(),
|
||||
|
@ -1666,6 +1664,23 @@ macro_rules! nop_lift {
|
|||
};
|
||||
}
|
||||
|
||||
// Can't use the macros as we have reuse the `substs` here.
|
||||
//
|
||||
// See `intern_type_list` for more info.
|
||||
impl<'a, 'tcx> Lift<'tcx> for &'a List<Ty<'a>> {
|
||||
type Lifted = &'tcx List<Ty<'tcx>>;
|
||||
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
|
||||
if self.is_empty() {
|
||||
return Some(List::empty());
|
||||
}
|
||||
if tcx.interners.substs.contains_pointer_to(&InternedInSet(self.as_substs())) {
|
||||
Some(unsafe { mem::transmute(self) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! nop_list_lift {
|
||||
($set:ident; $ty:ty => $lifted:ty) => {
|
||||
impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> {
|
||||
|
@ -1690,7 +1705,6 @@ nop_lift! {const_; Const<'a> => Const<'tcx>}
|
|||
nop_lift_old! {const_allocation; &'a Allocation => &'tcx Allocation}
|
||||
nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>}
|
||||
|
||||
nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>}
|
||||
nop_list_lift! {poly_existential_predicates; ty::Binder<'a, ExistentialPredicate<'a>> => ty::Binder<'tcx, ExistentialPredicate<'tcx>>}
|
||||
nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>}
|
||||
nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>}
|
||||
|
@ -2189,7 +2203,6 @@ macro_rules! slice_interners {
|
|||
}
|
||||
|
||||
slice_interners!(
|
||||
type_list: _intern_type_list(Ty<'tcx>),
|
||||
substs: _intern_substs(GenericArg<'tcx>),
|
||||
canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>),
|
||||
poly_existential_predicates:
|
||||
|
@ -2611,7 +2624,19 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}
|
||||
|
||||
pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List<Ty<'tcx>> {
|
||||
if ts.is_empty() { List::empty() } else { self._intern_type_list(ts) }
|
||||
if ts.is_empty() {
|
||||
List::empty()
|
||||
} else {
|
||||
// Actually intern type lists as lists of `GenericArg`s.
|
||||
//
|
||||
// Transmuting from `Ty<'tcx>` to `GenericArg<'tcx>` is sound
|
||||
// as explained in ty_slice_as_generic_arg`. With this,
|
||||
// we guarantee that even when transmuting between `List<Ty<'tcx>>`
|
||||
// and `List<GenericArg<'tcx>>`, the uniqueness requirement for
|
||||
// lists is upheld.
|
||||
let substs = self._intern_substs(ty::subst::ty_slice_as_generic_args(ts));
|
||||
substs.try_as_type_list().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intern_substs(self, ts: &[GenericArg<'tcx>]) -> &'tcx List<GenericArg<'tcx>> {
|
||||
|
|
|
@ -61,6 +61,28 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArg<'t
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArgKind<'tcx> {
|
||||
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
|
||||
match self {
|
||||
// WARNING: We dedup cache the `HashStable` results for `List`
|
||||
// while ignoring types and freely transmute
|
||||
// between `List<Ty<'tcx>>` and `List<GenericArg<'tcx>>`.
|
||||
// See `fn intern_type_list` for more details.
|
||||
//
|
||||
// We therefore hash types without adding a hash for their discriminant.
|
||||
ty::subst::GenericArgKind::Type(ty) => ty.hash_stable(hcx, hasher),
|
||||
ty::subst::GenericArgKind::Const(ct) => {
|
||||
mem::discriminant(self).hash_stable(hcx, hasher);
|
||||
ct.hash_stable(hcx, hasher);
|
||||
}
|
||||
ty::subst::GenericArgKind::Lifetime(lt) => {
|
||||
mem::discriminant(self).hash_stable(hcx, hasher);
|
||||
lt.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
|
||||
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
|
||||
mem::discriminant(self).hash_stable(hcx, hasher);
|
||||
|
|
|
@ -20,6 +20,7 @@ use std::marker::PhantomData;
|
|||
use std::mem;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::ControlFlow;
|
||||
use std::slice;
|
||||
|
||||
/// An entity in the Rust type system, which can be one of
|
||||
/// several kinds (types, lifetimes, and consts).
|
||||
|
@ -40,13 +41,37 @@ const TYPE_TAG: usize = 0b00;
|
|||
const REGION_TAG: usize = 0b01;
|
||||
const CONST_TAG: usize = 0b10;
|
||||
|
||||
#[derive(Debug, TyEncodable, TyDecodable, PartialEq, Eq, PartialOrd, Ord, HashStable)]
|
||||
#[derive(Debug, TyEncodable, TyDecodable, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum GenericArgKind<'tcx> {
|
||||
Lifetime(ty::Region<'tcx>),
|
||||
Type(Ty<'tcx>),
|
||||
Const(ty::Const<'tcx>),
|
||||
}
|
||||
|
||||
/// This function goes from `&'a [Ty<'tcx>]` to `&'a [GenericArg<'tcx>]`
|
||||
///
|
||||
/// This is sound as, for types, `GenericArg` is just
|
||||
/// `NonZeroUsize::new_unchecked(ty as *const _ as usize)`.
|
||||
pub fn ty_slice_as_generic_args<'a, 'tcx>(ts: &'a [Ty<'tcx>]) -> &'a [GenericArg<'tcx>] {
|
||||
assert_eq!(TYPE_TAG, 0);
|
||||
// SAFETY: the whole slice is valid and immutable.
|
||||
// `Ty` and `GenericArg` is explained above.
|
||||
unsafe { slice::from_raw_parts(ts.as_ptr().cast(), ts.len()) }
|
||||
}
|
||||
|
||||
impl<'tcx> List<Ty<'tcx>> {
|
||||
/// Allows to freely switch betwen `List<Ty<'tcx>>` and `List<GenericArg<'tcx>>`.
|
||||
///
|
||||
/// As lists are interned, `List<Ty<'tcx>>` and `List<GenericArg<'tcx>>` have
|
||||
/// be interned together, see `intern_type_list` for more details.
|
||||
#[inline]
|
||||
pub fn as_substs(&'tcx self) -> SubstsRef<'tcx> {
|
||||
assert_eq!(TYPE_TAG, 0);
|
||||
// SAFETY: `List<T>` is `#[repr(C)]`. `Ty` and `GenericArg` is explained above.
|
||||
unsafe { &*(self as *const List<Ty<'tcx>> as *const List<GenericArg<'tcx>>) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> GenericArgKind<'tcx> {
|
||||
#[inline]
|
||||
fn pack(self) -> GenericArg<'tcx> {
|
||||
|
@ -208,6 +233,17 @@ pub type InternalSubsts<'tcx> = List<GenericArg<'tcx>>;
|
|||
pub type SubstsRef<'tcx> = &'tcx InternalSubsts<'tcx>;
|
||||
|
||||
impl<'a, 'tcx> InternalSubsts<'tcx> {
|
||||
/// Checks whether all elements of this list are types, if so, transmute.
|
||||
pub fn try_as_type_list(&'tcx self) -> Option<&'tcx List<Ty<'tcx>>> {
|
||||
if self.iter().all(|arg| matches!(arg.unpack(), GenericArgKind::Type(_))) {
|
||||
assert_eq!(TYPE_TAG, 0);
|
||||
// SAFETY: All elements are types, see `List<Ty<'tcx>>::as_substs`.
|
||||
Some(unsafe { &*(self as *const List<GenericArg<'tcx>> as *const List<Ty<'tcx>>) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Interpret these substitutions as the substitutions of a closure type.
|
||||
/// Closure substitutions have a particular structure controlled by the
|
||||
/// compiler that encodes information like the signature and closure kind;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue