-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
safely transmute<&List<Ty<'tcx>>, &List<GenericArg<'tcx>>>
#93505
Changes from 1 commit
a9c1ab8
1245131
880343c
7d5d6c0
758f4e7
c909b6d
80f56cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. heh, this is...a cute pun. |
||
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>> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
lcnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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(_))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe checking the pointer tag directly would make it possible for LLVM to optimize this? Something like Adding |
||
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; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given the transmute inside it, in instances such as this one, I personally like to spell out the aliases explicitly:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i generally agree, though, considering that all other
Lift
impls also don't do that i am going to keep the code as is for now.