rustc_metadata/rmeta/
table.rs

1use rustc_hir::def::CtorOf;
2use rustc_index::Idx;
3
4use crate::rmeta::*;
5
6pub(super) trait IsDefault: Default {
7    fn is_default(&self) -> bool;
8}
9
10impl<T> IsDefault for Option<T> {
11    fn is_default(&self) -> bool {
12        self.is_none()
13    }
14}
15
16impl IsDefault for AttrFlags {
17    fn is_default(&self) -> bool {
18        self.is_empty()
19    }
20}
21
22impl IsDefault for bool {
23    fn is_default(&self) -> bool {
24        !self
25    }
26}
27
28impl IsDefault for ty::Asyncness {
29    fn is_default(&self) -> bool {
30        match self {
31            ty::Asyncness::Yes => false,
32            ty::Asyncness::No => true,
33        }
34    }
35}
36
37impl IsDefault for hir::Constness {
38    fn is_default(&self) -> bool {
39        match self {
40            rustc_hir::Constness::Const => false,
41            rustc_hir::Constness::NotConst => true,
42        }
43    }
44}
45
46impl IsDefault for u32 {
47    fn is_default(&self) -> bool {
48        *self == 0
49    }
50}
51
52impl IsDefault for u64 {
53    fn is_default(&self) -> bool {
54        *self == 0
55    }
56}
57
58impl<T> IsDefault for LazyArray<T> {
59    fn is_default(&self) -> bool {
60        self.num_elems == 0
61    }
62}
63
64impl IsDefault for UnusedGenericParams {
65    fn is_default(&self) -> bool {
66        // UnusedGenericParams encodes the *un*usedness as a bitset.
67        // This means that 0 corresponds to all bits used, which is indeed the default.
68        let is_default = self.bits() == 0;
69        debug_assert_eq!(is_default, self.all_used());
70        is_default
71    }
72}
73
74/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
75/// Used mainly for Lazy positions and lengths.
76///
77/// Invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
78/// but this has no impact on safety.
79/// In debug builds, this invariant is checked in `[TableBuilder::set]`
80pub(super) trait FixedSizeEncoding: IsDefault {
81    /// This should be `[u8; BYTE_LEN]`;
82    /// Cannot use an associated `const BYTE_LEN: usize` instead due to const eval limitations.
83    type ByteArray;
84
85    fn from_bytes(b: &Self::ByteArray) -> Self;
86    fn write_to_bytes(self, b: &mut Self::ByteArray);
87}
88
89impl FixedSizeEncoding for u64 {
90    type ByteArray = [u8; 8];
91
92    #[inline]
93    fn from_bytes(b: &[u8; 8]) -> Self {
94        Self::from_le_bytes(*b)
95    }
96
97    #[inline]
98    fn write_to_bytes(self, b: &mut [u8; 8]) {
99        *b = self.to_le_bytes();
100    }
101}
102
103macro_rules! fixed_size_enum {
104    ($ty:ty { $(($($pat:tt)*))* } $( unreachable { $(($($upat:tt)*))+ } )?) => {
105        impl FixedSizeEncoding for Option<$ty> {
106            type ByteArray = [u8;1];
107
108            #[inline]
109            fn from_bytes(b: &[u8;1]) -> Self {
110                use $ty::*;
111                if b[0] == 0 {
112                    return None;
113                }
114                match b[0] - 1 {
115                    $(${index()} => Some($($pat)*),)*
116                    _ => panic!("Unexpected {} code: {:?}", stringify!($ty), b[0]),
117                }
118            }
119
120            #[inline]
121            fn write_to_bytes(self, b: &mut [u8;1]) {
122                use $ty::*;
123                b[0] = match self {
124                    None => unreachable!(),
125                    $(Some($($pat)*) => 1 + ${index()},)*
126                    $(Some($($($upat)*)|+) => unreachable!(),)?
127                }
128            }
129        }
130    }
131}
132
133// Workaround; need const traits to construct bitflags in a const
134macro_rules! const_macro_kinds {
135    ($($name:ident),+$(,)?) => (MacroKinds::from_bits_truncate($(MacroKinds::$name.bits())|+))
136}
137const MACRO_KINDS_ATTR_BANG: MacroKinds = const_macro_kinds!(ATTR, BANG);
138const MACRO_KINDS_DERIVE_BANG: MacroKinds = const_macro_kinds!(DERIVE, BANG);
139const MACRO_KINDS_DERIVE_ATTR: MacroKinds = const_macro_kinds!(DERIVE, ATTR);
140const MACRO_KINDS_DERIVE_ATTR_BANG: MacroKinds = const_macro_kinds!(DERIVE, ATTR, BANG);
141// Ensure that we get a compilation error if MacroKinds gets extended without updating metadata.
142const _: () = assert!(MACRO_KINDS_DERIVE_ATTR_BANG.is_all());
143
144fixed_size_enum! {
145    DefKind {
146        ( Mod                                      )
147        ( Struct                                   )
148        ( Union                                    )
149        ( Enum                                     )
150        ( Variant                                  )
151        ( Trait                                    )
152        ( TyAlias                                  )
153        ( ForeignTy                                )
154        ( TraitAlias                               )
155        ( AssocTy                                  )
156        ( TyParam                                  )
157        ( Fn                                       )
158        ( Const                                    )
159        ( ConstParam                               )
160        ( AssocFn                                  )
161        ( AssocConst                               )
162        ( ExternCrate                              )
163        ( Use                                      )
164        ( ForeignMod                               )
165        ( AnonConst                                )
166        ( InlineConst                              )
167        ( OpaqueTy                                 )
168        ( Field                                    )
169        ( LifetimeParam                            )
170        ( GlobalAsm                                )
171        ( Impl { of_trait: false }                 )
172        ( Impl { of_trait: true }                  )
173        ( Closure                                  )
174        ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Not, nested: false } )
175        ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Not, nested: false } )
176        ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Mut, nested: false } )
177        ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Mut, nested: false } )
178        ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Not, nested: true } )
179        ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Not, nested: true } )
180        ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Mut, nested: true } )
181        ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Mut, nested: true } )
182        ( Ctor(CtorOf::Struct, CtorKind::Fn)       )
183        ( Ctor(CtorOf::Struct, CtorKind::Const)    )
184        ( Ctor(CtorOf::Variant, CtorKind::Fn)      )
185        ( Ctor(CtorOf::Variant, CtorKind::Const)   )
186        ( Macro(MacroKinds::BANG)                  )
187        ( Macro(MacroKinds::ATTR)                  )
188        ( Macro(MacroKinds::DERIVE)                )
189        ( Macro(MACRO_KINDS_ATTR_BANG)             )
190        ( Macro(MACRO_KINDS_DERIVE_ATTR)           )
191        ( Macro(MACRO_KINDS_DERIVE_BANG)           )
192        ( Macro(MACRO_KINDS_DERIVE_ATTR_BANG)      )
193        ( SyntheticCoroutineBody                   )
194    } unreachable {
195        ( Macro(_)                                 )
196    }
197}
198
199fixed_size_enum! {
200    hir::Constness {
201        ( NotConst )
202        ( Const    )
203    }
204}
205
206fixed_size_enum! {
207    hir::Defaultness {
208        ( Final                        )
209        ( Default { has_value: false } )
210        ( Default { has_value: true }  )
211    }
212}
213
214fixed_size_enum! {
215    hir::Safety {
216        ( Unsafe )
217        ( Safe   )
218    }
219}
220
221fixed_size_enum! {
222    ty::Asyncness {
223        ( Yes )
224        ( No  )
225    }
226}
227
228fixed_size_enum! {
229    hir::CoroutineKind {
230        ( Coroutine(hir::Movability::Movable)                                          )
231        ( Coroutine(hir::Movability::Static)                                           )
232        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Block)        )
233        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Fn)           )
234        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Closure)      )
235        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Block)      )
236        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Fn)         )
237        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Closure)    )
238        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Block)   )
239        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Fn)      )
240        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Closure) )
241    }
242}
243
244fixed_size_enum! {
245    MacroKind {
246        ( Attr   )
247        ( Bang   )
248        ( Derive )
249    }
250}
251
252// We directly encode RawDefId because using a `LazyValue` would incur a 50% overhead in the worst case.
253impl FixedSizeEncoding for Option<RawDefId> {
254    type ByteArray = [u8; 8];
255
256    #[inline]
257    fn from_bytes(encoded: &[u8; 8]) -> Self {
258        let (index, krate) = decode_interleaved(encoded);
259        let krate = u32::from_le_bytes(krate);
260        if krate == 0 {
261            return None;
262        }
263        let index = u32::from_le_bytes(index);
264
265        Some(RawDefId { krate: krate - 1, index })
266    }
267
268    #[inline]
269    fn write_to_bytes(self, dest: &mut [u8; 8]) {
270        match self {
271            None => unreachable!(),
272            Some(RawDefId { krate, index }) => {
273                debug_assert!(krate < u32::MAX);
274                // CrateNum is less than `CrateNum::MAX_AS_U32`.
275                let krate = (krate + 1).to_le_bytes();
276                let index = index.to_le_bytes();
277
278                // CrateNum is usually much smaller than the index within the crate, so put it in
279                // the second slot.
280                encode_interleaved(index, krate, dest);
281            }
282        }
283    }
284}
285
286impl FixedSizeEncoding for AttrFlags {
287    type ByteArray = [u8; 1];
288
289    #[inline]
290    fn from_bytes(b: &[u8; 1]) -> Self {
291        AttrFlags::from_bits_truncate(b[0])
292    }
293
294    #[inline]
295    fn write_to_bytes(self, b: &mut [u8; 1]) {
296        debug_assert!(!self.is_default());
297        b[0] = self.bits();
298    }
299}
300
301impl FixedSizeEncoding for bool {
302    type ByteArray = [u8; 1];
303
304    #[inline]
305    fn from_bytes(b: &[u8; 1]) -> Self {
306        b[0] != 0
307    }
308
309    #[inline]
310    fn write_to_bytes(self, b: &mut [u8; 1]) {
311        debug_assert!(!self.is_default());
312        b[0] = self as u8
313    }
314}
315
316impl FixedSizeEncoding for ty::Asyncness {
317    type ByteArray = [u8; 1];
318
319    #[inline]
320    fn from_bytes(b: &[u8; 1]) -> Self {
321        match b[0] {
322            0 => ty::Asyncness::No,
323            1 => ty::Asyncness::Yes,
324            _ => unreachable!(),
325        }
326    }
327
328    #[inline]
329    fn write_to_bytes(self, b: &mut [u8; 1]) {
330        debug_assert!(!self.is_default());
331        b[0] = match self {
332            ty::Asyncness::No => 0,
333            ty::Asyncness::Yes => 1,
334        }
335    }
336}
337
338impl FixedSizeEncoding for hir::Constness {
339    type ByteArray = [u8; 1];
340
341    #[inline]
342    fn from_bytes(b: &[u8; 1]) -> Self {
343        match b[0] {
344            0 => hir::Constness::NotConst,
345            1 => hir::Constness::Const,
346            _ => unreachable!(),
347        }
348    }
349
350    #[inline]
351    fn write_to_bytes(self, b: &mut [u8; 1]) {
352        debug_assert!(!self.is_default());
353        b[0] = match self {
354            hir::Constness::NotConst => 0,
355            hir::Constness::Const => 1,
356        }
357    }
358}
359
360// NOTE(eddyb) there could be an impl for `usize`, which would enable a more
361// generic `LazyValue<T>` impl, but in the general case we might not need / want
362// to fit every `usize` in `u32`.
363impl<T> FixedSizeEncoding for Option<LazyValue<T>> {
364    type ByteArray = [u8; 8];
365
366    #[inline]
367    fn from_bytes(b: &[u8; 8]) -> Self {
368        let position = NonZero::new(u64::from_bytes(b) as usize)?;
369        Some(LazyValue::from_position(position))
370    }
371
372    #[inline]
373    fn write_to_bytes(self, b: &mut [u8; 8]) {
374        match self {
375            None => unreachable!(),
376            Some(lazy) => {
377                let position = lazy.position.get();
378                let position: u64 = position.try_into().unwrap();
379                position.write_to_bytes(b)
380            }
381        }
382    }
383}
384
385impl<T> LazyArray<T> {
386    #[inline]
387    fn write_to_bytes_impl(self, dest: &mut [u8; 16]) {
388        let position = (self.position.get() as u64).to_le_bytes();
389        let len = (self.num_elems as u64).to_le_bytes();
390
391        encode_interleaved(position, len, dest)
392    }
393
394    fn from_bytes_impl(position: &[u8; 8], meta: &[u8; 8]) -> Option<LazyArray<T>> {
395        let position = NonZero::new(u64::from_bytes(position) as usize)?;
396        let len = u64::from_bytes(meta) as usize;
397        Some(LazyArray::from_position_and_num_elems(position, len))
398    }
399}
400
401// Interleaving the bytes of the two integers exposes trailing bytes in the first integer
402// to the varint scheme that we use for tables.
403#[inline]
404fn decode_interleaved<const N: usize, const M: usize>(encoded: &[u8; N]) -> ([u8; M], [u8; M]) {
405    assert_eq!(M * 2, N);
406    let mut first = [0u8; M];
407    let mut second = [0u8; M];
408    for i in 0..M {
409        first[i] = encoded[2 * i];
410        second[i] = encoded[2 * i + 1];
411    }
412    (first, second)
413}
414
415// Element width is selected at runtime on a per-table basis by omitting trailing
416// zero bytes in table elements. This works very naturally when table elements are
417// simple numbers but sometimes we have a pair of integers. If naively encoded, the second element
418// would shield the trailing zeroes in the first. Interleaving the bytes exposes trailing zeroes in
419// both to the optimization.
420//
421// Prefer passing a and b such that `b` is usually smaller.
422#[inline]
423fn encode_interleaved<const N: usize, const M: usize>(a: [u8; M], b: [u8; M], dest: &mut [u8; N]) {
424    assert_eq!(M * 2, N);
425    for i in 0..M {
426        dest[2 * i] = a[i];
427        dest[2 * i + 1] = b[i];
428    }
429}
430
431impl<T> FixedSizeEncoding for LazyArray<T> {
432    type ByteArray = [u8; 16];
433
434    #[inline]
435    fn from_bytes(b: &[u8; 16]) -> Self {
436        let (position, meta) = decode_interleaved(b);
437
438        if meta == [0; 8] {
439            return Default::default();
440        }
441        LazyArray::from_bytes_impl(&position, &meta).unwrap()
442    }
443
444    #[inline]
445    fn write_to_bytes(self, b: &mut [u8; 16]) {
446        assert!(!self.is_default());
447        self.write_to_bytes_impl(b)
448    }
449}
450
451impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
452    type ByteArray = [u8; 16];
453
454    #[inline]
455    fn from_bytes(b: &[u8; 16]) -> Self {
456        let (position, meta) = decode_interleaved(b);
457
458        LazyArray::from_bytes_impl(&position, &meta)
459    }
460
461    #[inline]
462    fn write_to_bytes(self, b: &mut [u8; 16]) {
463        match self {
464            None => unreachable!(),
465            Some(lazy) => lazy.write_to_bytes_impl(b),
466        }
467    }
468}
469
470/// Helper for constructing a table's serialization (also see `Table`).
471pub(super) struct TableBuilder<I: Idx, T: FixedSizeEncoding> {
472    width: usize,
473    blocks: IndexVec<I, T::ByteArray>,
474    _marker: PhantomData<T>,
475}
476
477impl<I: Idx, T: FixedSizeEncoding> Default for TableBuilder<I, T> {
478    fn default() -> Self {
479        TableBuilder { width: 0, blocks: Default::default(), _marker: PhantomData }
480    }
481}
482
483impl<I: Idx, const N: usize, T> TableBuilder<I, Option<T>>
484where
485    Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
486{
487    pub(crate) fn set_some(&mut self, i: I, value: T) {
488        self.set(i, Some(value))
489    }
490}
491
492impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBuilder<I, T> {
493    /// Sets the table value if it is not default.
494    /// ATTENTION: For optimization default values are simply ignored by this function, because
495    /// right now metadata tables never need to reset non-default values to default. If such need
496    /// arises in the future then a new method (e.g. `clear` or `reset`) will need to be introduced
497    /// for doing that explicitly.
498    pub(crate) fn set(&mut self, i: I, value: T) {
499        #[cfg(debug_assertions)]
500        {
501            debug_assert!(
502                T::from_bytes(&[0; N]).is_default(),
503                "expected all-zeroes to decode to the default value, as per the invariant of FixedSizeEncoding"
504            );
505        }
506        if !value.is_default() {
507            // FIXME(eddyb) investigate more compact encodings for sparse tables.
508            // On the PR @michaelwoerister mentioned:
509            // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
510            // > trick (i.e. divide things into buckets of 32 or 64 items and then
511            // > store bit-masks of which item in each bucket is actually serialized).
512            let block = self.blocks.ensure_contains_elem(i, || [0; N]);
513            value.write_to_bytes(block);
514            if self.width != N {
515                let width = N - trailing_zeros(block);
516                self.width = self.width.max(width);
517            }
518        }
519    }
520
521    pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
522        let pos = buf.position();
523
524        let width = self.width;
525        for block in &self.blocks {
526            buf.write_with(|dest| {
527                *dest = *block;
528                width
529            });
530        }
531
532        LazyTable::from_position_and_encoded_size(
533            NonZero::new(pos).unwrap(),
534            width,
535            self.blocks.len(),
536        )
537    }
538}
539
540fn trailing_zeros(x: &[u8]) -> usize {
541    x.iter().rev().take_while(|b| **b == 0).count()
542}
543
544impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]> + ParameterizedOverTcx>
545    LazyTable<I, T>
546where
547    for<'tcx> T::Value<'tcx>: FixedSizeEncoding<ByteArray = [u8; N]>,
548{
549    /// Given the metadata, extract out the value at a particular index (if any).
550    pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
551        // Access past the end of the table returns a Default
552        if i.index() >= self.len {
553            return Default::default();
554        }
555
556        let width = self.width;
557        let start = self.position.get() + (width * i.index());
558        let end = start + width;
559        let bytes = &metadata.blob()[start..end];
560
561        if let Ok(fixed) = bytes.try_into() {
562            FixedSizeEncoding::from_bytes(fixed)
563        } else {
564            let mut fixed = [0u8; N];
565            fixed[..width].copy_from_slice(bytes);
566            FixedSizeEncoding::from_bytes(&fixed)
567        }
568    }
569
570    /// Size of the table in entries, including possible gaps.
571    pub(super) fn size(&self) -> usize {
572        self.len
573    }
574}