Skip to content

Commit a9c1ab8

Browse files
committed
safely transmute<&List<Ty<'tcx>>, &List<GenericArg<'tcx>>>
1 parent 45e2c28 commit a9c1ab8

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

compiler/rustc_middle/src/ty/context.rs

+30-5
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ pub struct CtxtInterners<'tcx> {
101101
// Specifically use a speedy hash algorithm for these hash sets, since
102102
// they're accessed quite often.
103103
type_: InternedSet<'tcx, TyS<'tcx>>,
104-
type_list: InternedSet<'tcx, List<Ty<'tcx>>>,
105104
substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
106105
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
107106
region: InternedSet<'tcx, RegionKind>,
@@ -129,7 +128,6 @@ impl<'tcx> CtxtInterners<'tcx> {
129128
CtxtInterners {
130129
arena,
131130
type_: Default::default(),
132-
type_list: Default::default(),
133131
substs: Default::default(),
134132
region: Default::default(),
135133
poly_existential_predicates: Default::default(),
@@ -1666,6 +1664,23 @@ macro_rules! nop_lift {
16661664
};
16671665
}
16681666

1667+
// Can't use the macros as we have reuse the `substs` here.
1668+
//
1669+
// See `intern_type_list` for more info.
1670+
impl<'a, 'tcx> Lift<'tcx> for &'a List<Ty<'a>> {
1671+
type Lifted = &'tcx List<Ty<'tcx>>;
1672+
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
1673+
if self.is_empty() {
1674+
return Some(List::empty());
1675+
}
1676+
if tcx.interners.substs.contains_pointer_to(&InternedInSet(self.as_substs())) {
1677+
Some(unsafe { mem::transmute(self) })
1678+
} else {
1679+
None
1680+
}
1681+
}
1682+
}
1683+
16691684
macro_rules! nop_list_lift {
16701685
($set:ident; $ty:ty => $lifted:ty) => {
16711686
impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> {
@@ -1690,7 +1705,6 @@ nop_lift! {const_; Const<'a> => Const<'tcx>}
16901705
nop_lift_old! {const_allocation; &'a Allocation => &'tcx Allocation}
16911706
nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>}
16921707

1693-
nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>}
16941708
nop_list_lift! {poly_existential_predicates; ty::Binder<'a, ExistentialPredicate<'a>> => ty::Binder<'tcx, ExistentialPredicate<'tcx>>}
16951709
nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>}
16961710
nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>}
@@ -2189,7 +2203,6 @@ macro_rules! slice_interners {
21892203
}
21902204

21912205
slice_interners!(
2192-
type_list: _intern_type_list(Ty<'tcx>),
21932206
substs: _intern_substs(GenericArg<'tcx>),
21942207
canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>),
21952208
poly_existential_predicates:
@@ -2611,7 +2624,19 @@ impl<'tcx> TyCtxt<'tcx> {
26112624
}
26122625

26132626
pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List<Ty<'tcx>> {
2614-
if ts.is_empty() { List::empty() } else { self._intern_type_list(ts) }
2627+
if ts.is_empty() {
2628+
List::empty()
2629+
} else {
2630+
// Actually intern type lists as lists of `GenericArg`s.
2631+
//
2632+
// Transmuting from `Ty<'tcx>` to `GenericArg<'tcx>` is sound
2633+
// as explained in ty_slice_as_generic_arg`. With this,
2634+
// we guarantee that even when transmuting between `List<Ty<'tcx>>`
2635+
// and `List<GenericArg<'tcx>>`, the uniqueness requirement for
2636+
// lists is upheld.
2637+
let substs = self._intern_substs(ty::subst::ty_slice_as_generic_args(ts));
2638+
substs.try_as_type_list().unwrap()
2639+
}
26152640
}
26162641

26172642
pub fn intern_substs(self, ts: &[GenericArg<'tcx>]) -> &'tcx List<GenericArg<'tcx>> {

compiler/rustc_middle/src/ty/impls_ty.rs

+22
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,28 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArg<'t
6161
}
6262
}
6363

64+
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArgKind<'tcx> {
65+
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
66+
match self {
67+
// WARNING: We dedup cache the `HashStable` results for `List`
68+
// while ignoring types and freely transmute
69+
// between `List<Ty<'tcx>>` and `List<GenericArg<'tcx>>`.
70+
// See `fn intern_type_list` for more details.
71+
//
72+
// We therefore hash types without adding a hash for their discriminant.
73+
ty::subst::GenericArgKind::Type(ty) => ty.hash_stable(hcx, hasher),
74+
ty::subst::GenericArgKind::Const(ct) => {
75+
mem::discriminant(self).hash_stable(hcx, hasher);
76+
ct.hash_stable(hcx, hasher);
77+
}
78+
ty::subst::GenericArgKind::Lifetime(lt) => {
79+
mem::discriminant(self).hash_stable(hcx, hasher);
80+
lt.hash_stable(hcx, hasher);
81+
}
82+
}
83+
}
84+
}
85+
6486
impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
6587
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
6688
mem::discriminant(self).hash_stable(hcx, hasher);

compiler/rustc_middle/src/ty/subst.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::marker::PhantomData;
2020
use std::mem;
2121
use std::num::NonZeroUsize;
2222
use std::ops::ControlFlow;
23+
use std::slice;
2324

2425
/// An entity in the Rust type system, which can be one of
2526
/// several kinds (types, lifetimes, and consts).
@@ -40,13 +41,37 @@ const TYPE_TAG: usize = 0b00;
4041
const REGION_TAG: usize = 0b01;
4142
const CONST_TAG: usize = 0b10;
4243

43-
#[derive(Debug, TyEncodable, TyDecodable, PartialEq, Eq, PartialOrd, Ord, HashStable)]
44+
#[derive(Debug, TyEncodable, TyDecodable, PartialEq, Eq, PartialOrd, Ord)]
4445
pub enum GenericArgKind<'tcx> {
4546
Lifetime(ty::Region<'tcx>),
4647
Type(Ty<'tcx>),
4748
Const(ty::Const<'tcx>),
4849
}
4950

51+
/// This function goes from `&'a [Ty<'tcx>]` to `&'a [GenericArg<'tcx>]`
52+
///
53+
/// This is sound as, for types, `GenericArg` is just
54+
/// `NonZeroUsize::new_unchecked(ty as *const _ as usize)`.
55+
pub fn ty_slice_as_generic_args<'a, 'tcx>(ts: &'a [Ty<'tcx>]) -> &'a [GenericArg<'tcx>] {
56+
assert_eq!(TYPE_TAG, 0);
57+
// SAFETY: the whole slice is valid and immutable.
58+
// `Ty` and `GenericArg` is explained above.
59+
unsafe { slice::from_raw_parts(ts.as_ptr().cast(), ts.len()) }
60+
}
61+
62+
impl<'tcx> List<Ty<'tcx>> {
63+
/// Allows to freely switch betwen `List<Ty<'tcx>>` and `List<GenericArg<'tcx>>`.
64+
///
65+
/// As lists are interned, `List<Ty<'tcx>>` and `List<GenericArg<'tcx>>` have
66+
/// be interned together, see `intern_type_list` for more details.
67+
#[inline]
68+
pub fn as_substs(&'tcx self) -> SubstsRef<'tcx> {
69+
assert_eq!(TYPE_TAG, 0);
70+
// SAFETY: `List<T>` is `#[repr(C)]`. `Ty` and `GenericArg` is explained above.
71+
unsafe { &*(self as *const List<Ty<'tcx>> as *const List<GenericArg<'tcx>>) }
72+
}
73+
}
74+
5075
impl<'tcx> GenericArgKind<'tcx> {
5176
#[inline]
5277
fn pack(self) -> GenericArg<'tcx> {
@@ -208,6 +233,17 @@ pub type InternalSubsts<'tcx> = List<GenericArg<'tcx>>;
208233
pub type SubstsRef<'tcx> = &'tcx InternalSubsts<'tcx>;
209234

210235
impl<'a, 'tcx> InternalSubsts<'tcx> {
236+
/// Checks whether all elements of this list are types, if so, transmute.
237+
pub fn try_as_type_list(&'tcx self) -> Option<&'tcx List<Ty<'tcx>>> {
238+
if self.iter().all(|arg| matches!(arg.unpack(), GenericArgKind::Type(_))) {
239+
assert_eq!(TYPE_TAG, 0);
240+
// SAFETY: All elements are types, see `List<Ty<'tcx>>::as_substs`.
241+
Some(unsafe { &*(self as *const List<GenericArg<'tcx>> as *const List<Ty<'tcx>>) })
242+
} else {
243+
None
244+
}
245+
}
246+
211247
/// Interpret these substitutions as the substitutions of a closure type.
212248
/// Closure substitutions have a particular structure controlled by the
213249
/// compiler that encodes information like the signature and closure kind;

0 commit comments

Comments
 (0)