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
|
// Specifically use a speedy hash algorithm for these hash sets, since
|
||||||
// they're accessed quite often.
|
// they're accessed quite often.
|
||||||
type_: InternedSet<'tcx, TyS<'tcx>>,
|
type_: InternedSet<'tcx, TyS<'tcx>>,
|
||||||
type_list: InternedSet<'tcx, List<Ty<'tcx>>>,
|
|
||||||
substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
|
substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
|
||||||
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
|
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
|
||||||
region: InternedSet<'tcx, RegionKind>,
|
region: InternedSet<'tcx, RegionKind>,
|
||||||
|
@ -129,7 +128,6 @@ impl<'tcx> CtxtInterners<'tcx> {
|
||||||
CtxtInterners {
|
CtxtInterners {
|
||||||
arena,
|
arena,
|
||||||
type_: Default::default(),
|
type_: Default::default(),
|
||||||
type_list: Default::default(),
|
|
||||||
substs: Default::default(),
|
substs: Default::default(),
|
||||||
region: Default::default(),
|
region: Default::default(),
|
||||||
poly_existential_predicates: 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 {
|
macro_rules! nop_list_lift {
|
||||||
($set:ident; $ty:ty => $lifted:ty) => {
|
($set:ident; $ty:ty => $lifted:ty) => {
|
||||||
impl<'a, 'tcx> Lift<'tcx> for &'a List<$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_old! {const_allocation; &'a Allocation => &'tcx Allocation}
|
||||||
nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>}
|
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! {poly_existential_predicates; ty::Binder<'a, ExistentialPredicate<'a>> => ty::Binder<'tcx, ExistentialPredicate<'tcx>>}
|
||||||
nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>}
|
nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>}
|
||||||
nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>}
|
nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>}
|
||||||
|
@ -2189,7 +2203,6 @@ macro_rules! slice_interners {
|
||||||
}
|
}
|
||||||
|
|
||||||
slice_interners!(
|
slice_interners!(
|
||||||
type_list: _intern_type_list(Ty<'tcx>),
|
|
||||||
substs: _intern_substs(GenericArg<'tcx>),
|
substs: _intern_substs(GenericArg<'tcx>),
|
||||||
canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>),
|
canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>),
|
||||||
poly_existential_predicates:
|
poly_existential_predicates:
|
||||||
|
@ -2611,7 +2624,19 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List<Ty<'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>> {
|
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 {
|
impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
|
||||||
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
|
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
|
||||||
mem::discriminant(self).hash_stable(hcx, hasher);
|
mem::discriminant(self).hash_stable(hcx, hasher);
|
||||||
|
|
|
@ -20,6 +20,7 @@ use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
/// An entity in the Rust type system, which can be one of
|
/// An entity in the Rust type system, which can be one of
|
||||||
/// several kinds (types, lifetimes, and consts).
|
/// several kinds (types, lifetimes, and consts).
|
||||||
|
@ -40,13 +41,37 @@ const TYPE_TAG: usize = 0b00;
|
||||||
const REGION_TAG: usize = 0b01;
|
const REGION_TAG: usize = 0b01;
|
||||||
const CONST_TAG: usize = 0b10;
|
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> {
|
pub enum GenericArgKind<'tcx> {
|
||||||
Lifetime(ty::Region<'tcx>),
|
Lifetime(ty::Region<'tcx>),
|
||||||
Type(Ty<'tcx>),
|
Type(Ty<'tcx>),
|
||||||
Const(ty::Const<'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> {
|
impl<'tcx> GenericArgKind<'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pack(self) -> GenericArg<'tcx> {
|
fn pack(self) -> GenericArg<'tcx> {
|
||||||
|
@ -208,6 +233,17 @@ pub type InternalSubsts<'tcx> = List<GenericArg<'tcx>>;
|
||||||
pub type SubstsRef<'tcx> = &'tcx InternalSubsts<'tcx>;
|
pub type SubstsRef<'tcx> = &'tcx InternalSubsts<'tcx>;
|
||||||
|
|
||||||
impl<'a, '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.
|
/// Interpret these substitutions as the substitutions of a closure type.
|
||||||
/// Closure substitutions have a particular structure controlled by the
|
/// Closure substitutions have a particular structure controlled by the
|
||||||
/// compiler that encodes information like the signature and closure kind;
|
/// compiler that encodes information like the signature and closure kind;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue