blob: 0cf5820959ee531c3651f15c9c62858c6db8bee6 [file] [log] [blame]
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,
}
}
}