1use std::alloc::Layout;
2use std::cmp::Ordering;
3use std::hash::{Hash, Hasher};
4use std::ops::Deref;
5use std::{fmt, iter, mem, ptr, slice};
67use rustc_data_structures::aligned::{Aligned, align_of};
8use rustc_data_structures::sync::DynSync;
9use rustc_serialize::{Encodable, Encoder};
10use rustc_type_ir::FlagComputation;
1112use super::{DebruijnIndex, TyCtxt, TypeFlags};
13use crate::arena::Arena;
1415/// `List<T>` is a bit like `&[T]`, but with some critical differences.
16/// - IMPORTANT: Every `List<T>` is *required* to have unique contents. The
17/// type's correctness relies on this, *but it does not enforce it*.
18/// Therefore, any code that creates a `List<T>` must ensure uniqueness
19/// itself. In practice this is achieved by interning.
20/// - The length is stored within the `List<T>`, so `&List<Ty>` is a thin
21/// pointer.
22/// - Because of this, you cannot get a `List<T>` that is a sub-list of another
23/// `List<T>`. You can get a sub-slice `&[T]`, however.
24/// - `List<T>` can be used with `TaggedRef`, which is useful within
25/// structs whose size must be minimized.
26/// - Because of the uniqueness assumption, we can use the address of a
27/// `List<T>` for faster equality comparisons and hashing.
28/// - `T` must be `Copy`. This lets `List<T>` be stored in a dropless arena and
29/// iterators return a `T` rather than a `&T`.
30/// - `T` must not be zero-sized.
31pub type List<T> = RawList<(), T>;
3233/// A generic type that can be used to prepend a [`List`] with some header.
34///
35/// The header will be ignored for value-based operations like [`PartialEq`],
36/// [`Hash`] and [`Encodable`].
37#[repr(C)]
38pub struct RawList<H, T> {
39 skel: ListSkeleton<H, T>,
4041// `List`/`RawList` is variable-sized. So we want it to be an unsized
42 // type because calling `size_of::<List<Foo>>` would be dangerous.
43 //
44 // We also want `&List`/`&RawList` to be thin pointers.
45 //
46 // A field with an extern type is a hacky way to achieve this. (See
47 // https://github.com/rust-lang/rust/pull/154399#issuecomment-4157036415
48 // for some discussion.) This field is never directly manipulated because
49 // `RawList` instances are created with manual memory layout in
50 // `from_arena`.
51_extern_ty: ExternTy,
52}
5354/// A [`RawList`] without the unsized tail. This type is used for layout computation
55/// and constructing empty lists.
56#[repr(C)]
57struct ListSkeleton<H, T> {
58 header: H,
59 len: usize,
60/// Although this claims to be a zero-length array, in practice `len`
61 /// elements are actually present. This is achieved with manual memory
62 /// layout in `from_arena`. See also the comment on `RawList::_extern_ty`.
63data: [T; 0],
64}
6566impl<T> Defaultfor &List<T> {
67fn default() -> Self {
68List::empty()
69 }
70}
7172unsafe extern "C" {
73type ExternTy;
74}
7576impl<H, T> RawList<H, T> {
77#[inline(always)]
78pub fn len(&self) -> usize {
79self.skel.len
80 }
8182#[inline(always)]
83pub fn as_slice(&self) -> &[T] {
84self85 }
8687/// Allocates a list from `arena` and copies the contents of `slice` into it.
88 ///
89 /// WARNING: the contents *must be unique*, such that no list with these
90 /// contents has been previously created. If not, operations such as `eq`
91 /// and `hash` might give incorrect results.
92 ///
93 /// Panics if `T` is `Drop`, or `T` is zero-sized, or the slice is empty
94 /// (because the empty list exists statically, and is available via
95 /// `empty()`).
96#[inline]
97pub(super) fn from_arena<'tcx>(
98 arena: &'tcx Arena<'tcx>,
99 header: H,
100 slice: &[T],
101 ) -> &'tcx RawList<H, T>
102where
103T: Copy,
104 {
105if !!mem::needs_drop::<T>() {
::core::panicking::panic("assertion failed: !mem::needs_drop::<T>()")
};assert!(!mem::needs_drop::<T>());
106if !(size_of::<T>() != 0) {
::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
107if !!slice.is_empty() {
::core::panicking::panic("assertion failed: !slice.is_empty()")
};assert!(!slice.is_empty());
108109let (layout, _offset) =
110Layout::new::<ListSkeleton<H, T>>().extend(Layout::for_value::<[T]>(slice)).unwrap();
111112let mem = arena.dropless.alloc_raw(layout) as *mut RawList<H, T>;
113unsafe {
114// Write the header
115(&raw mut (*mem).skel.header).write(header);
116117// Write the length
118(&raw mut (*mem).skel.len).write(slice.len());
119120// Write the elements
121(&raw mut (*mem).skel.data)
122 .cast::<T>()
123 .copy_from_nonoverlapping(slice.as_ptr(), slice.len());
124125&*mem126 }
127 }
128129// If this method didn't exist, we would use `slice.iter` due to
130 // deref coercion.
131 //
132 // This would be weird, as `self.into_iter` iterates over `T` directly.
133#[inline(always)]
134pub fn iter(&self) -> <&'_ RawList<H, T> as IntoIterator>::IntoIter135where
136T: Copy,
137 {
138self.into_iter()
139 }
140}
141142impl<'a, H, T: Copy> rustc_type_ir::inherent::SliceLikefor &'a RawList<H, T> {
143type Item = T;
144145type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>;
146147fn iter(self) -> Self::IntoIter {
148 (*self).iter()
149 }
150151fn as_slice(&self) -> &[Self::Item] {
152 (*self).as_slice()
153 }
154}
155156macro_rules!impl_list_empty {
157 ($header_ty:ty, $header_init:expr) => {
158impl<T> RawList<$header_ty, T> {
159/// Returns a reference to the (per header unique, static) empty list.
160#[inline(always)]
161pub fn empty<'a>() -> &'a RawList<$header_ty, T> {
162#[repr(align(64))]
163struct MaxAlign;
164165static EMPTY: ListSkeleton<$header_ty, MaxAlign> =
166 ListSkeleton { header: $header_init, len: 0, data: [] };
167168assert!(align_of::<T>() <= align_of::<MaxAlign>());
169170// SAFETY: `EMPTY` is sufficiently aligned to be an empty list for all
171 // types with `align_of(T) <= align_of(MaxAlign)`, which we checked above.
172unsafe { &*((&raw const EMPTY) as *const RawList<$header_ty, T>) }
173 }
174 }
175 };
176}
177178impl<T> RawList<(), T> {
/// Returns a reference to the (per header unique, static) empty list.
#[inline(always)]
pub fn empty<'a>() -> &'a RawList<(), T> {
#[repr(align(64))]
struct MaxAlign;
static EMPTY: ListSkeleton<(), MaxAlign> =
ListSkeleton { header: (), len: 0, data: [] };
if !(align_of::<T>() <= align_of::<MaxAlign>()) {
::core::panicking::panic("assertion failed: align_of::<T>() <= align_of::<MaxAlign>()")
};
unsafe { &*((&raw const EMPTY) as *const RawList<(), T>) }
}
}impl_list_empty!((), ());
179180impl<H, T: fmt::Debug> fmt::Debugfor RawList<H, T> {
181fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 (**self).fmt(f)
183 }
184}
185186impl<H, S: Encoder, T: Encodable<S>> Encodable<S> for RawList<H, T> {
187#[inline]
188fn encode(&self, s: &mut S) {
189 (**self).encode(s);
190 }
191}
192193impl<H, T: PartialEq> PartialEqfor RawList<H, T> {
194#[inline]
195fn eq(&self, other: &RawList<H, T>) -> bool {
196// Pointer equality implies list equality (due to the unique contents
197 // assumption).
198ptr::eq(self, other)
199 }
200}
201202impl<H, T: Eq> Eqfor RawList<H, T> {}
203204impl<H, T> Ordfor RawList<H, T>
205where
206T: Ord,
207{
208fn cmp(&self, other: &RawList<H, T>) -> Ordering {
209// Pointer equality implies list equality (due to the unique contents
210 // assumption), but the contents must be compared otherwise.
211if self == other { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) }
212 }
213}
214215impl<H, T> PartialOrdfor RawList<H, T>
216where
217T: PartialOrd,
218{
219fn partial_cmp(&self, other: &RawList<H, T>) -> Option<Ordering> {
220// Pointer equality implies list equality (due to the unique contents
221 // assumption), but the contents must be compared otherwise.
222if self == other {
223Some(Ordering::Equal)
224 } else {
225 <[T] as PartialOrd>::partial_cmp(&**self, &**other)
226 }
227 }
228}
229230impl<Hdr, T> Hashfor RawList<Hdr, T> {
231#[inline]
232fn hash<H: Hasher>(&self, s: &mut H) {
233// Pointer hashing is sufficient (due to the unique contents
234 // assumption).
235ptr::from_ref(self).hash(s)
236 }
237}
238239impl<H, T> Dereffor RawList<H, T> {
240type Target = [T];
241#[inline(always)]
242fn deref(&self) -> &[T] {
243self.as_ref()
244 }
245}
246247impl<H, T> AsRef<[T]> for RawList<H, T> {
248#[inline(always)]
249fn as_ref(&self) -> &[T] {
250let data_ptr = (&raw const self.skel.data).cast::<T>();
251// SAFETY: `data_ptr` has the same provenance as `self` and can therefore
252 // access the `self.skel.len` elements stored at `self.skel.data`.
253 // Note that we specifically don't reborrow `&self.skel.data`, because that
254 // would give us a pointer with provenance over 0 bytes.
255unsafe { slice::from_raw_parts(data_ptr, self.skel.len) }
256 }
257}
258259impl<'a, H, T: Copy> IntoIteratorfor &'a RawList<H, T> {
260type Item = T;
261type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>;
262#[inline(always)]
263fn into_iter(self) -> Self::IntoIter {
264self[..].iter().copied()
265 }
266}
267268unsafe impl<H: Sync, T: Sync> Syncfor RawList<H, T> {}
269270// We need this because `List` uses the extern type `ExternTy`.
271unsafe impl<H: DynSync, T: DynSync> DynSyncfor RawList<H, T> {}
272273// Safety:
274// Layouts of `ListSkeleton<H, T>` and `RawList<H, T>` are the same, modulo the
275// `_extern_ty` field (which is never instantiated in practice). Therefore,
276// aligns of `ListSkeleton<H, T>` and `RawList<H, T>` must be the same.
277unsafe impl<H, T> Alignedfor RawList<H, T> {
278#[cfg(bootstrap)]
279const ALIGN: ptr::Alignment = align_of::<ListSkeleton<H, T>>();
280#[cfg(not(bootstrap))]
281const ALIGN: mem::Alignment = align_of::<ListSkeleton<H, T>>();
282}
283284/// A [`List`] that additionally stores type information inline to speed up
285/// [`TypeVisitableExt`](super::TypeVisitableExt) operations.
286pub type ListWithCachedTypeInfo<T> = RawList<TypeInfo, T>;
287288impl<T> ListWithCachedTypeInfo<T> {
289#[inline(always)]
290pub fn flags(&self) -> TypeFlags {
291self.skel.header.flags
292 }
293294#[inline(always)]
295pub fn outer_exclusive_binder(&self) -> DebruijnIndex {
296self.skel.header.outer_exclusive_binder
297 }
298}
299300impl<T> RawList<TypeInfo, T> {
/// Returns a reference to the (per header unique, static) empty list.
#[inline(always)]
pub fn empty<'a>() -> &'a RawList<TypeInfo, T> {
#[repr(align(64))]
struct MaxAlign;
static EMPTY: ListSkeleton<TypeInfo, MaxAlign> =
ListSkeleton { header: TypeInfo::empty(), len: 0, data: [] };
if !(align_of::<T>() <= align_of::<MaxAlign>()) {
::core::panicking::panic("assertion failed: align_of::<T>() <= align_of::<MaxAlign>()")
};
unsafe { &*((&raw const EMPTY) as *const RawList<TypeInfo, T>) }
}
}impl_list_empty!(TypeInfo, TypeInfo::empty());
301302/// The additional info that is stored in [`ListWithCachedTypeInfo`].
303#[repr(C)]
304#[derive(#[automatically_derived]
impl ::core::fmt::Debug for TypeInfo {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "TypeInfo",
"flags", &self.flags, "outer_exclusive_binder",
&&self.outer_exclusive_binder)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for TypeInfo {
#[inline]
fn clone(&self) -> TypeInfo {
let _: ::core::clone::AssertParamIsClone<TypeFlags>;
let _: ::core::clone::AssertParamIsClone<DebruijnIndex>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for TypeInfo { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for TypeInfo {
#[inline]
fn eq(&self, other: &TypeInfo) -> bool {
self.flags == other.flags &&
self.outer_exclusive_binder == other.outer_exclusive_binder
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for TypeInfo {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<TypeFlags>;
let _: ::core::cmp::AssertParamIsEq<DebruijnIndex>;
}
}Eq)]
305pub struct TypeInfo {
306 flags: TypeFlags,
307 outer_exclusive_binder: DebruijnIndex,
308}
309310impl TypeInfo {
311const fn empty() -> Self {
312Self { flags: TypeFlags::empty(), outer_exclusive_binder: super::INNERMOST }
313 }
314}
315316impl<'tcx> From<FlagComputation<TyCtxt<'tcx>>> for TypeInfo {
317fn from(computation: FlagComputation<TyCtxt<'tcx>>) -> TypeInfo {
318TypeInfo {
319 flags: computation.flags,
320 outer_exclusive_binder: computation.outer_exclusive_binder,
321 }
322 }
323}
324325#[cfg(target_pointer_width = "64")]
326mod size_asserts {
327use rustc_data_structures::static_assert_size;
328329use super::*;
330// tidy-alphabetical-start
331const _: [(); 8] = [(); ::std::mem::size_of::<&List<u32>>()];static_assert_size!(&List<u32>, 8); // thin pointer
332const _: [(); 8] = [(); ::std::mem::size_of::<&RawList<u8, u32>>()];static_assert_size!(&RawList<u8, u32>, 8); // thin pointer
333 // tidy-alphabetical-end
334}