1
Fork 0

safely transmute<&List<Ty<'tcx>>, &List<GenericArg<'tcx>>>

This commit is contained in:
lcnr 2022-02-07 16:05:17 +01:00
parent 45e2c2881d
commit a9c1ab82f5
3 changed files with 89 additions and 6 deletions

View file

@ -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>> {

View file

@ -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);

View file

@ -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;