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
156impl<'tcx> rustc_type_ir::inherent::BoundVarKinds<TyCtxt<'tcx>>
157    for &'tcx RawList<(), crate::ty::BoundVariableKind<'tcx>>
158{
159    fn from_vars(
160        tcx: TyCtxt<'tcx>,
161        iter: impl IntoIterator<Item = crate::ty::BoundVariableKind<'tcx>>,
162    ) -> Self {
163        tcx.mk_bound_variable_kinds_from_iter(iter.into_iter())
164    }
165}
166
167macro_rules! impl_list_empty {
168    ($header_ty:ty, $header_init:expr) => {
169        impl<T> RawList<$header_ty, T> {
170            /// Returns a reference to the (per header unique, static) empty list.
171            #[inline(always)]
172            pub fn empty<'a>() -> &'a RawList<$header_ty, T> {
173                #[repr(align(64))]
174                struct MaxAlign;
175
176                static EMPTY: ListSkeleton<$header_ty, MaxAlign> =
177                    ListSkeleton { header: $header_init, len: 0, data: [] };
178
179                assert!(align_of::<T>() <= align_of::<MaxAlign>());
180
181                // SAFETY: `EMPTY` is sufficiently aligned to be an empty list for all
182                // types with `align_of(T) <= align_of(MaxAlign)`, which we checked above.
183                unsafe { &*((&raw const EMPTY) as *const RawList<$header_ty, T>) }
184            }
185        }
186    };
187}
188
189impl<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!((), ());
190
191impl<H, T: fmt::Debug> fmt::Debug for RawList<H, T> {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        (**self).fmt(f)
194    }
195}
196
197impl<H, S: Encoder, T: Encodable<S>> Encodable<S> for RawList<H, T> {
198    #[inline]
199    fn encode(&self, s: &mut S) {
200        (**self).encode(s);
201    }
202}
203
204impl<H, T: PartialEq> PartialEq for RawList<H, T> {
205    #[inline]
206    fn eq(&self, other: &RawList<H, T>) -> bool {
207        // Pointer equality implies list equality (due to the unique contents
208        // assumption).
209        ptr::eq(self, other)
210    }
211}
212
213impl<H, T: Eq> Eq for RawList<H, T> {}
214
215impl<H, T> Ord for RawList<H, T>
216where
217    T: Ord,
218{
219    fn cmp(&self, other: &RawList<H, T>) -> 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 { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) }
223    }
224}
225
226impl<H, T> PartialOrd for RawList<H, T>
227where
228    T: PartialOrd,
229{
230    fn partial_cmp(&self, other: &RawList<H, T>) -> Option<Ordering> {
231        // Pointer equality implies list equality (due to the unique contents
232        // assumption), but the contents must be compared otherwise.
233        if self == other {
234            Some(Ordering::Equal)
235        } else {
236            <[T] as PartialOrd>::partial_cmp(&**self, &**other)
237        }
238    }
239}
240
241impl<Hdr, T> Hash for RawList<Hdr, T> {
242    #[inline]
243    fn hash<H: Hasher>(&self, s: &mut H) {
244        // Pointer hashing is sufficient (due to the unique contents
245        // assumption).
246        ptr::from_ref(self).hash(s)
247    }
248}
249
250impl<H, T> Deref for RawList<H, T> {
251    type Target = [T];
252    #[inline(always)]
253    fn deref(&self) -> &[T] {
254        self.as_ref()
255    }
256}
257
258impl<H, T> AsRef<[T]> for RawList<H, T> {
259    #[inline(always)]
260    fn as_ref(&self) -> &[T] {
261        let data_ptr = (&raw const self.skel.data).cast::<T>();
262        // SAFETY: `data_ptr` has the same provenance as `self` and can therefore
263        // access the `self.skel.len` elements stored at `self.skel.data`.
264        // Note that we specifically don't reborrow `&self.skel.data`, because that
265        // would give us a pointer with provenance over 0 bytes.
266        unsafe { slice::from_raw_parts(data_ptr, self.skel.len) }
267    }
268}
269
270impl<'a, H, T: Copy> IntoIterator for &'a RawList<H, T> {
271    type Item = T;
272    type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>;
273    #[inline(always)]
274    fn into_iter(self) -> Self::IntoIter {
275        self[..].iter().copied()
276    }
277}
278
279unsafe impl<H: Sync, T: Sync> Sync for RawList<H, T> {}
280
281// We need this because `List` uses the extern type `ExternTy`.
282unsafe impl<H: DynSync, T: DynSync> DynSync for RawList<H, T> {}
283
284// Safety:
285// Layouts of `ListSkeleton<H, T>` and `RawList<H, T>` are the same, modulo the
286// `_extern_ty` field (which is never instantiated in practice). Therefore,
287// aligns of `ListSkeleton<H, T>` and `RawList<H, T>` must be the same.
288unsafe impl<H, T> Aligned for RawList<H, T> {
289    const ALIGN: mem::Alignment = align_of::<ListSkeleton<H, T>>();
290}
291
292/// A [`List`] that additionally stores type information inline to speed up
293/// [`TypeVisitableExt`](super::TypeVisitableExt) operations.
294pub type ListWithCachedTypeInfo<T> = RawList<TypeInfo, T>;
295
296impl<T> ListWithCachedTypeInfo<T> {
297    #[inline(always)]
298    pub fn flags(&self) -> TypeFlags {
299        self.skel.header.flags
300    }
301
302    #[inline(always)]
303    pub fn outer_exclusive_binder(&self) -> DebruijnIndex {
304        self.skel.header.outer_exclusive_binder
305    }
306}
307
308impl<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());
309
310/// The additional info that is stored in [`ListWithCachedTypeInfo`].
311#[repr(C)]
312#[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)]
313pub struct TypeInfo {
314    flags: TypeFlags,
315    outer_exclusive_binder: DebruijnIndex,
316}
317
318impl TypeInfo {
319    const fn empty() -> Self {
320        Self { flags: TypeFlags::empty(), outer_exclusive_binder: super::INNERMOST }
321    }
322}
323
324impl<'tcx> From<FlagComputation<TyCtxt<'tcx>>> for TypeInfo {
325    fn from(computation: FlagComputation<TyCtxt<'tcx>>) -> TypeInfo {
326        TypeInfo {
327            flags: computation.flags,
328            outer_exclusive_binder: computation.outer_exclusive_binder,
329        }
330    }
331}
332
333#[cfg(target_pointer_width = "64")]
334mod size_asserts {
335    use rustc_data_structures::static_assert_size;
336
337    use super::*;
338    // tidy-alphabetical-start
339    const _: [(); 8] = [(); ::std::mem::size_of::<&List<u32>>()];static_assert_size!(&List<u32>, 8); // thin pointer
340    const _: [(); 8] = [(); ::std::mem::size_of::<&RawList<u8, u32>>()];static_assert_size!(&RawList<u8, u32>, 8); // thin pointer
341    // tidy-alphabetical-end
342}