summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
blob: 49b1190ad46a3a009e9617c9057a898a11859385 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Definitions needed for computing data layout of types.

use std::cmp;

use la_arena::{Idx, RawIdx};
pub use rustc_abi::{
    Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
    LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
    TargetDataLayout, TargetDataLayoutErrors, WrappingRange,
};

use crate::LocalEnumVariantId;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);

impl rustc_index::vec::Idx for RustcEnumVariantIdx {
    fn new(idx: usize) -> Self {
        RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
    }

    fn index(self) -> usize {
        u32::from(self.0.into_raw()) as usize
    }
}

pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;

pub trait IntegerExt {
    fn repr_discr(
        dl: &TargetDataLayout,
        repr: &ReprOptions,
        min: i128,
        max: i128,
    ) -> Result<(Integer, bool), LayoutError>;
}

impl IntegerExt for Integer {
    /// Finds the appropriate Integer type and signedness for the given
    /// signed discriminant range and `#[repr]` attribute.
    /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
    /// that shouldn't affect anything, other than maybe debuginfo.
    fn repr_discr(
        dl: &TargetDataLayout,
        repr: &ReprOptions,
        min: i128,
        max: i128,
    ) -> Result<(Integer, bool), LayoutError> {
        // Theoretically, negative values could be larger in unsigned representation
        // than the unsigned representation of the signed minimum. However, if there
        // are any negative values, the only valid unsigned representation is u128
        // which can fit all i128 values, so the result remains unaffected.
        let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
        let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));

        if let Some(ity) = repr.int {
            let discr = Integer::from_attr(dl, ity);
            let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
            if discr < fit {
                return Err(LayoutError::UserError(
                    "Integer::repr_discr: `#[repr]` hint too small for \
                      discriminant range of enum "
                        .to_string(),
                ));
            }
            return Ok((discr, ity.is_signed()));
        }

        let at_least = if repr.c() {
            // This is usually I32, however it can be different on some platforms,
            // notably hexagon and arm-none/thumb-none
            dl.c_enum_min_size
        } else {
            // repr(Rust) enums try to be as small as possible
            Integer::I8
        };

        // If there are no negative values, we can use the unsigned fit.
        Ok(if min >= 0 {
            (cmp::max(unsigned_fit, at_least), false)
        } else {
            (cmp::max(signed_fit, at_least), true)
        })
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LayoutError {
    UserError(String),
    SizeOverflow,
    TargetLayoutNotAvailable,
    HasPlaceholder,
    NotImplemented,
    Unknown,
}