use std::alloc::Layout; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::{fmt, iter, mem, ptr, slice}; use rustc_data_structures::aligned::{Aligned, align_of}; use rustc_data_structures::sync::DynSync; use rustc_serialize::{Encodable, Encoder}; use rustc_type_ir::FlagComputation; use super::{DebruijnIndex, TyCtxt, TypeFlags}; use crate::arena::Arena; /// `List` is a bit like `&[T]`, but with some critical differences. /// - IMPORTANT: Every `List` is *required* to have unique contents. The /// type's correctness relies on this, *but it does not enforce it*. /// Therefore, any code that creates a `List` must ensure uniqueness /// itself. In practice this is achieved by interning. /// - The length is stored within the `List`, so `&List` is a thin /// pointer. /// - Because of this, you cannot get a `List` that is a sub-list of another /// `List`. You can get a sub-slice `&[T]`, however. /// - `List` can be used with `TaggedRef`, which is useful within /// structs whose size must be minimized. /// - Because of the uniqueness assumption, we can use the address of a /// `List` for faster equality comparisons and hashing. /// - `T` must be `Copy`. This lets `List` be stored in a dropless arena and /// iterators return a `T` rather than a `&T`. /// - `T` must not be zero-sized. pub type List = RawList<(), T>; /// A generic type that can be used to prepend a [`List`] with some header. /// /// The header will be ignored for value-based operations like [`PartialEq`], /// [`Hash`] and [`Encodable`]. #[repr(C)] pub struct RawList { skel: ListSkeleton, opaque: OpaqueListContents, } /// A [`RawList`] without the unsized tail. This type is used for layout computation /// and constructing empty lists. #[repr(C)] struct ListSkeleton { header: H, len: usize, /// Although this claims to be a zero-length array, in practice `len` /// elements are actually present. data: [T; 0], } impl Default for &List { fn default() -> Self { List::empty() } } unsafe extern "C" { /// A dummy type used to force `List` to be unsized while not requiring /// references to it be wide pointers. type OpaqueListContents; } impl RawList { #[inline(always)] pub fn len(&self) -> usize { self.skel.len } #[inline(always)] pub fn as_slice(&self) -> &[T] { self } /// Allocates a list from `arena` and copies the contents of `slice` into it. /// /// WARNING: the contents *must be unique*, such that no list with these /// contents has been previously created. If not, operations such as `eq` /// and `hash` might give incorrect results. /// /// Panics if `T` is `Drop`, or `T` is zero-sized, or the slice is empty /// (because the empty list exists statically, and is available via /// `empty()`). #[inline] pub(super) fn from_arena<'tcx>( arena: &'tcx Arena<'tcx>, header: H, slice: &[T], ) -> &'tcx RawList where T: Copy, { assert!(!mem::needs_drop::()); assert!(size_of::() != 0); assert!(!slice.is_empty()); let (layout, _offset) = Layout::new::>().extend(Layout::for_value::<[T]>(slice)).unwrap(); let mem = arena.dropless.alloc_raw(layout) as *mut RawList; unsafe { // Write the header (&raw mut (*mem).skel.header).write(header); // Write the length (&raw mut (*mem).skel.len).write(slice.len()); // Write the elements (&raw mut (*mem).skel.data) .cast::() .copy_from_nonoverlapping(slice.as_ptr(), slice.len()); &*mem } } // If this method didn't exist, we would use `slice.iter` due to // deref coercion. // // This would be weird, as `self.into_iter` iterates over `T` directly. #[inline(always)] pub fn iter(&self) -> <&'_ RawList as IntoIterator>::IntoIter where T: Copy, { self.into_iter() } } impl<'a, H, T: Copy> rustc_type_ir::inherent::SliceLike for &'a RawList { type Item = T; type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>; fn iter(self) -> Self::IntoIter { (*self).iter() } fn as_slice(&self) -> &[Self::Item] { (*self).as_slice() } } macro_rules! impl_list_empty { ($header_ty:ty, $header_init:expr) => { impl RawList<$header_ty, T> { /// Returns a reference to the (per header unique, static) empty list. #[inline(always)] pub fn empty<'a>() -> &'a RawList<$header_ty, T> { #[repr(align(64))] struct MaxAlign; static EMPTY: ListSkeleton<$header_ty, MaxAlign> = ListSkeleton { header: $header_init, len: 0, data: [] }; assert!(align_of::() <= align_of::()); // SAFETY: `EMPTY` is sufficiently aligned to be an empty list for all // types with `align_of(T) <= align_of(MaxAlign)`, which we checked above. unsafe { &*((&raw const EMPTY) as *const RawList<$header_ty, T>) } } } }; } impl_list_empty!((), ()); impl fmt::Debug for RawList { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl> Encodable for RawList { #[inline] fn encode(&self, s: &mut S) { (**self).encode(s); } } impl PartialEq for RawList { #[inline] fn eq(&self, other: &RawList) -> bool { // Pointer equality implies list equality (due to the unique contents // assumption). ptr::eq(self, other) } } impl Eq for RawList {} impl Ord for RawList where T: Ord, { fn cmp(&self, other: &RawList) -> Ordering { // Pointer equality implies list equality (due to the unique contents // assumption), but the contents must be compared otherwise. if self == other { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) } } } impl PartialOrd for RawList where T: PartialOrd, { fn partial_cmp(&self, other: &RawList) -> Option { // Pointer equality implies list equality (due to the unique contents // assumption), but the contents must be compared otherwise. if self == other { Some(Ordering::Equal) } else { <[T] as PartialOrd>::partial_cmp(&**self, &**other) } } } impl Hash for RawList { #[inline] fn hash(&self, s: &mut H) { // Pointer hashing is sufficient (due to the unique contents // assumption). ptr::from_ref(self).hash(s) } } impl Deref for RawList { type Target = [T]; #[inline(always)] fn deref(&self) -> &[T] { self.as_ref() } } impl AsRef<[T]> for RawList { #[inline(always)] fn as_ref(&self) -> &[T] { let data_ptr = (&raw const self.skel.data).cast::(); // SAFETY: `data_ptr` has the same provenance as `self` and can therefore // access the `self.skel.len` elements stored at `self.skel.data`. // Note that we specifically don't reborrow `&self.skel.data`, because that // would give us a pointer with provenance over 0 bytes. unsafe { slice::from_raw_parts(data_ptr, self.skel.len) } } } impl<'a, H, T: Copy> IntoIterator for &'a RawList { type Item = T; type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>; #[inline(always)] fn into_iter(self) -> Self::IntoIter { self[..].iter().copied() } } unsafe impl Sync for RawList {} // We need this since `List` uses extern type `OpaqueListContents`. unsafe impl DynSync for RawList {} // Safety: // Layouts of `ListSkeleton` and `RawList` are the same, modulo opaque tail, // thus aligns of `ListSkeleton` and `RawList` must be the same. unsafe impl Aligned for RawList { const ALIGN: ptr::Alignment = align_of::>(); } /// A [`List`] that additionally stores type information inline to speed up /// [`TypeVisitableExt`](super::TypeVisitableExt) operations. pub type ListWithCachedTypeInfo = RawList; impl ListWithCachedTypeInfo { #[inline(always)] pub fn flags(&self) -> TypeFlags { self.skel.header.flags } #[inline(always)] pub fn outer_exclusive_binder(&self) -> DebruijnIndex { self.skel.header.outer_exclusive_binder } } impl_list_empty!(TypeInfo, TypeInfo::empty()); /// The additional info that is stored in [`ListWithCachedTypeInfo`]. #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct TypeInfo { flags: TypeFlags, outer_exclusive_binder: DebruijnIndex, } impl TypeInfo { const fn empty() -> Self { Self { flags: TypeFlags::empty(), outer_exclusive_binder: super::INNERMOST } } } impl<'tcx> From>> for TypeInfo { fn from(computation: FlagComputation>) -> TypeInfo { TypeInfo { flags: computation.flags, outer_exclusive_binder: computation.outer_exclusive_binder, } } }