Skip to main content

rustc_middle/ty/
list.rs

1use std::alloc::Layout;
2use std::cmp::Ordering;
3use std::hash::{Hash, Hasher};
4use std::ops::Deref;
5use std::{fmt, iter, mem, ptr, slice};
6
7use rustc_data_structures::aligned::{Aligned, align_of};
8use rustc_data_structures::sync::DynSync;
9use rustc_serialize::{Encodable, Encoder};
10use rustc_type_ir::FlagComputation;
11
12use super::{DebruijnIndex, TyCtxt, TypeFlags};
13use crate::arena::Arena;
14
15/// `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>;
32
33/// 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>,
40
41    // `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}
53
54/// 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`.
63    data: [T; 0],
64}
65
66impl<T> Default for &List<T> {
67    fn default() -> Self {
68        List::empty()
69    }
70}
71
72unsafe extern "C" {
73    type ExternTy;
74}
75
76impl<H, T> RawList<H, T> {
77    #[inline(always)]
78    pub fn len(&self) -> usize {
79        self.skel.len
80    }
81
82    #[inline(always)]
83    pub fn as_slice(&self) -> &[T] {
84        self
85    }
86
87    /// 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]
97    pub(super) fn from_arena<'tcx>(
98        arena: &'tcx Arena<'tcx>,
99        header: H,
100        slice: &[T],
101    ) -> &'tcx RawList<H, T>
102    where
103        T: Copy,
104    {
105        if !!mem::needs_drop::<T>() {
    ::core::panicking::panic("assertion failed: !mem::needs_drop::<T>()")
};assert!(!mem::needs_drop::<T>());
106        if !(size_of::<T>() != 0) {
    ::core::panicking::panic("assertion failed: size_of::<T>() != 0")
};assert!(size_of::<T>() != 0);
107        if !!slice.is_empty() {
    ::core::panicking::panic("assertion failed: !slice.is_empty()")
};assert!(!slice.is_empty());
108
109        let (layout, _offset) =
110            Layout::new::<ListSkeleton<H, T>>().extend(Layout::for_value::<[T]>(slice)).unwrap();
111
112        let mem = arena.dropless.alloc_raw(layout) as *mut RawList<H, T>;
113        unsafe {
114            // Write the header
115            (&raw mut (*mem).skel.header).write(header);
116
117            // Write the length
118            (&raw mut (*mem).skel.len).write(slice.len());
119
120            // Write the elements
121            (&raw mut (*mem).skel.data)
122                .cast::<T>()
123                .copy_from_nonoverlapping(slice.as_ptr(), slice.len());
124
125            &*mem
126        }
127    }
128
129    // 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)]
134    pub fn iter(&self) -> <&'_ RawList<H, T> as IntoIterator>::IntoIter
135    where
136        T: Copy,
137    {
138        self.into_iter()
139    }
140}
141
142impl<'a, H, T: Copy> rustc_type_ir::inherent::SliceLike for &'a RawList<H, T> {
143    type Item = T;
144
145    type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>;
146
147    fn iter(self) -> Self::IntoIter {
148        (*self).iter()
149    }
150
151    fn as_slice(&self) -> &[Self::Item] {
152        (*self).as_slice()
153    }
154}
155
156macro_rules! impl_list_empty {
157    ($header_ty:ty, $header_init:expr) => {
158        impl<T> RawList<$header_ty, T> {
159            /// Returns a reference to the (per header unique, static) empty list.
160            #[inline(always)]
161            pub fn empty<'a>() -> &'a RawList<$header_ty, T> {
162                #[repr(align(64))]
163                struct MaxAlign;
164
165                static EMPTY: ListSkeleton<$header_ty, MaxAlign> =
166                    ListSkeleton { header: $header_init, len: 0, data: [] };
167
168                assert!(align_of::<T>() <= align_of::<MaxAlign>());
169
170                // 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.
172                unsafe { &*((&raw const EMPTY) as *const RawList<$header_ty, T>) }
173            }
174        }
175    };
176}
177
178impl<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!((), ());
179
180impl<H, T: fmt::Debug> fmt::Debug for RawList<H, T> {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        (**self).fmt(f)
183    }
184}
185
186impl<H, S: Encoder, T: Encodable<S>> Encodable<S> for RawList<H, T> {
187    #[inline]
188    fn encode(&self, s: &mut S) {
189        (**self).encode(s);
190    }
191}
192
193impl<H, T: PartialEq> PartialEq for RawList<H, T> {
194    #[inline]
195    fn eq(&self, other: &RawList<H, T>) -> bool {
196        // Pointer equality implies list equality (due to the unique contents
197        // assumption).
198        ptr::eq(self, other)
199    }
200}
201
202impl<H, T: Eq> Eq for RawList<H, T> {}
203
204impl<H, T> Ord for RawList<H, T>
205where
206    T: Ord,
207{
208    fn 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.
211        if self == other { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) }
212    }
213}
214
215impl<H, T> PartialOrd for RawList<H, T>
216where
217    T: PartialOrd,
218{
219    fn 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.
222        if self == other {
223            Some(Ordering::Equal)
224        } else {
225            <[T] as PartialOrd>::partial_cmp(&**self, &**other)
226        }
227    }
228}
229
230impl<Hdr, T> Hash for RawList<Hdr, T> {
231    #[inline]
232    fn hash<H: Hasher>(&self, s: &mut H) {
233        // Pointer hashing is sufficient (due to the unique contents
234        // assumption).
235        ptr::from_ref(self).hash(s)
236    }
237}
238
239impl<H, T> Deref for RawList<H, T> {
240    type Target = [T];
241    #[inline(always)]
242    fn deref(&self) -> &[T] {
243        self.as_ref()
244    }
245}
246
247impl<H, T> AsRef<[T]> for RawList<H, T> {
248    #[inline(always)]
249    fn as_ref(&self) -> &[T] {
250        let 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.
255        unsafe { slice::from_raw_parts(data_ptr, self.skel.len) }
256    }
257}
258
259impl<'a, H, T: Copy> IntoIterator for &'a RawList<H, T> {
260    type Item = T;
261    type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>;
262    #[inline(always)]
263    fn into_iter(self) -> Self::IntoIter {
264        self[..].iter().copied()
265    }
266}
267
268unsafe impl<H: Sync, T: Sync> Sync for RawList<H, T> {}
269
270// We need this because `List` uses the extern type `ExternTy`.
271unsafe impl<H: DynSync, T: DynSync> DynSync for RawList<H, T> {}
272
273// 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> Aligned for RawList<H, T> {
278    #[cfg(bootstrap)]
279    const ALIGN: ptr::Alignment = align_of::<ListSkeleton<H, T>>();
280    #[cfg(not(bootstrap))]
281    const ALIGN: mem::Alignment = align_of::<ListSkeleton<H, T>>();
282}
283
284/// A [`List`] that additionally stores type information inline to speed up
285/// [`TypeVisitableExt`](super::TypeVisitableExt) operations.
286pub type ListWithCachedTypeInfo<T> = RawList<TypeInfo, T>;
287
288impl<T> ListWithCachedTypeInfo<T> {
289    #[inline(always)]
290    pub fn flags(&self) -> TypeFlags {
291        self.skel.header.flags
292    }
293
294    #[inline(always)]
295    pub fn outer_exclusive_binder(&self) -> DebruijnIndex {
296        self.skel.header.outer_exclusive_binder
297    }
298}
299
300impl<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());
301
302/// 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}
309
310impl TypeInfo {
311    const fn empty() -> Self {
312        Self { flags: TypeFlags::empty(), outer_exclusive_binder: super::INNERMOST }
313    }
314}
315
316impl<'tcx> From<FlagComputation<TyCtxt<'tcx>>> for TypeInfo {
317    fn from(computation: FlagComputation<TyCtxt<'tcx>>) -> TypeInfo {
318        TypeInfo {
319            flags: computation.flags,
320            outer_exclusive_binder: computation.outer_exclusive_binder,
321        }
322    }
323}
324
325#[cfg(target_pointer_width = "64")]
326mod size_asserts {
327    use rustc_data_structures::static_assert_size;
328
329    use super::*;
330    // tidy-alphabetical-start
331    const _: [(); 8] = [(); ::std::mem::size_of::<&List<u32>>()];static_assert_size!(&List<u32>, 8); // thin pointer
332    const _: [(); 8] = [(); ::std::mem::size_of::<&RawList<u8, u32>>()];static_assert_size!(&RawList<u8, u32>, 8); // thin pointer
333    // tidy-alphabetical-end
334}