| 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<T>` is a bit like `&[T]`, but with some critical differences. | |
| /// - IMPORTANT: Every `List<T>` 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<T>` must ensure uniqueness | |
| /// itself. In practice this is achieved by interning. | |
| /// - The length is stored within the `List<T>`, so `&List<Ty>` is a thin | |
| /// pointer. | |
| /// - Because of this, you cannot get a `List<T>` that is a sub-list of another | |
| /// `List<T>`. You can get a sub-slice `&[T]`, however. | |
| /// - `List<T>` 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<T>` for faster equality comparisons and hashing. | |
| /// - `T` must be `Copy`. This lets `List<T>` be stored in a dropless arena and | |
| /// iterators return a `T` rather than a `&T`. | |
| /// - `T` must not be zero-sized. | |
| pub type List<T> = 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<H, T> { | |
| skel: ListSkeleton<H, T>, | |
| opaque: OpaqueListContents, | |
| } | |
| /// A [`RawList`] without the unsized tail. This type is used for layout computation | |
| /// and constructing empty lists. | |
| #[repr(C)] | |
| struct ListSkeleton<H, T> { | |
| header: H, | |
| len: usize, | |
| /// Although this claims to be a zero-length array, in practice `len` | |
| /// elements are actually present. | |
| data: [T; 0], | |
| } | |
| impl<T> Default for &List<T> { | |
| 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<H, T> RawList<H, T> { | |
| #[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<H, T> | |
| where | |
| T: Copy, | |
| { | |
| assert!(!mem::needs_drop::<T>()); | |
| assert!(size_of::<T>() != 0); | |
| assert!(!slice.is_empty()); | |
| let (layout, _offset) = | |
| Layout::new::<ListSkeleton<H, T>>().extend(Layout::for_value::<[T]>(slice)).unwrap(); | |
| let mem = arena.dropless.alloc_raw(layout) as *mut RawList<H, T>; | |
| 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::<T>() | |
| .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<H, T> as IntoIterator>::IntoIter | |
| where | |
| T: Copy, | |
| { | |
| self.into_iter() | |
| } | |
| } | |
| impl<'a, H, T: Copy> rustc_type_ir::inherent::SliceLike for &'a RawList<H, T> { | |
| 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<T> 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::<T>() <= align_of::<MaxAlign>()); | |
| // 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<H, T: fmt::Debug> fmt::Debug for RawList<H, T> { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| (**self).fmt(f) | |
| } | |
| } | |
| impl<H, S: Encoder, T: Encodable<S>> Encodable<S> for RawList<H, T> { | |
| #[inline] | |
| fn encode(&self, s: &mut S) { | |
| (**self).encode(s); | |
| } | |
| } | |
| impl<H, T: PartialEq> PartialEq for RawList<H, T> { | |
| #[inline] | |
| fn eq(&self, other: &RawList<H, T>) -> bool { | |
| // Pointer equality implies list equality (due to the unique contents | |
| // assumption). | |
| ptr::eq(self, other) | |
| } | |
| } | |
| impl<H, T: Eq> Eq for RawList<H, T> {} | |
| impl<H, T> Ord for RawList<H, T> | |
| where | |
| T: Ord, | |
| { | |
| fn cmp(&self, other: &RawList<H, T>) -> 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<H, T> PartialOrd for RawList<H, T> | |
| where | |
| T: PartialOrd, | |
| { | |
| fn partial_cmp(&self, other: &RawList<H, T>) -> Option<Ordering> { | |
| // 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<Hdr, T> Hash for RawList<Hdr, T> { | |
| #[inline] | |
| fn hash<H: Hasher>(&self, s: &mut H) { | |
| // Pointer hashing is sufficient (due to the unique contents | |
| // assumption). | |
| ptr::from_ref(self).hash(s) | |
| } | |
| } | |
| impl<H, T> Deref for RawList<H, T> { | |
| type Target = [T]; | |
| #[inline(always)] | |
| fn deref(&self) -> &[T] { | |
| self.as_ref() | |
| } | |
| } | |
| impl<H, T> AsRef<[T]> for RawList<H, T> { | |
| #[inline(always)] | |
| fn as_ref(&self) -> &[T] { | |
| let data_ptr = (&raw const self.skel.data).cast::<T>(); | |
| // 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<H, T> { | |
| 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<H: Sync, T: Sync> Sync for RawList<H, T> {} | |
| // We need this since `List` uses extern type `OpaqueListContents`. | |
| unsafe impl<H: DynSync, T: DynSync> DynSync for RawList<H, T> {} | |
| // Safety: | |
| // Layouts of `ListSkeleton<H, T>` and `RawList<H, T>` are the same, modulo opaque tail, | |
| // thus aligns of `ListSkeleton<H, T>` and `RawList<H, T>` must be the same. | |
| unsafe impl<H, T> Aligned for RawList<H, T> { | |
| const ALIGN: ptr::Alignment = align_of::<ListSkeleton<H, T>>(); | |
| } | |
| /// A [`List`] that additionally stores type information inline to speed up | |
| /// [`TypeVisitableExt`](super::TypeVisitableExt) operations. | |
| pub type ListWithCachedTypeInfo<T> = RawList<TypeInfo, T>; | |
| impl<T> ListWithCachedTypeInfo<T> { | |
| #[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<FlagComputation<TyCtxt<'tcx>>> for TypeInfo { | |
| fn from(computation: FlagComputation<TyCtxt<'tcx>>) -> TypeInfo { | |
| TypeInfo { | |
| flags: computation.flags, | |
| outer_exclusive_binder: computation.outer_exclusive_binder, | |
| } | |
| } | |
| } |