summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
blob: 23166a5a5223a98ef561513cb6b7e3cc455f92ab (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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Compute the binary representation of structs, unions and enums

use std::ops::Bound;

use hir_def::{
    adt::VariantData,
    layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
    AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
};
use la_arena::RawIdx;
use smallvec::SmallVec;

use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution};

use super::{layout_of_ty, LayoutCx};

pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
    RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
}

pub fn layout_of_adt_query(
    db: &dyn HirDatabase,
    def: AdtId,
    subst: Substitution,
) -> Result<Layout, LayoutError> {
    let cx = LayoutCx { db, krate: def.module(db.upcast()).krate() };
    let dl = cx.current_data_layout();
    let handle_variant = |def: VariantId, var: &VariantData| {
        var.fields()
            .iter()
            .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
            .collect::<Result<Vec<_>, _>>()
    };
    let (variants, is_enum, is_union, repr) = match def {
        AdtId::StructId(s) => {
            let data = db.struct_data(s);
            let mut r = SmallVec::<[_; 1]>::new();
            r.push(handle_variant(s.into(), &data.variant_data)?);
            (r, false, false, data.repr.unwrap_or_default())
        }
        AdtId::UnionId(id) => {
            let data = db.union_data(id);
            let mut r = SmallVec::new();
            r.push(handle_variant(id.into(), &data.variant_data)?);
            (r, false, true, data.repr.unwrap_or_default())
        }
        AdtId::EnumId(e) => {
            let data = db.enum_data(e);
            let r = data
                .variants
                .iter()
                .map(|(idx, v)| {
                    handle_variant(
                        EnumVariantId { parent: e, local_id: idx }.into(),
                        &v.variant_data,
                    )
                })
                .collect::<Result<SmallVec<_>, _>>()?;
            (r, true, false, data.repr.unwrap_or_default())
        }
    };
    let variants =
        variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>();
    let variants = variants.iter().map(|x| x.iter().collect()).collect();
    if is_union {
        cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
    } else {
        cx.layout_of_struct_or_enum(
            &repr,
            &variants,
            is_enum,
            is_unsafe_cell(def, db),
            layout_scalar_valid_range(db, def),
            |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
            variants.iter_enumerated().filter_map(|(id, _)| {
                let AdtId::EnumId(e) = def else { return None };
                let d = match db
                    .const_eval_variant(EnumVariantId { parent: e, local_id: id.0 })
                    .ok()?
                {
                    crate::consteval::ComputedExpr::Literal(l) => match l {
                        hir_def::expr::Literal::Int(i, _) => i,
                        hir_def::expr::Literal::Uint(i, _) => i as i128,
                        _ => return None,
                    },
                    _ => return None,
                };
                Some((id, d))
            }),
            // FIXME: The current code for niche-filling relies on variant indices
            // instead of actual discriminants, so enums with
            // explicit discriminants (RFC #2363) would misbehave and we should disable
            // niche optimization for them.
            // The code that do it in rustc:
            // repr.inhibit_enum_layout_opt() || def
            //     .variants()
            //     .iter_enumerated()
            //     .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
            repr.inhibit_enum_layout_opt(),
            !is_enum
                && variants
                    .iter()
                    .next()
                    .and_then(|x| x.last().map(|x| x.is_unsized()))
                    .unwrap_or(true),
        )
        .ok_or(LayoutError::SizeOverflow)
    }
}

fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
    let attrs = db.attrs(def.into());
    let get = |name| {
        let attr = attrs.by_key(name).tt_values();
        for tree in attr {
            if let Some(x) = tree.token_trees.first() {
                if let Ok(x) = x.to_string().parse() {
                    return Bound::Included(x);
                }
            }
        }
        Bound::Unbounded
    };
    (get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end"))
}

pub fn layout_of_adt_recover(
    _: &dyn HirDatabase,
    _: &[String],
    _: &AdtId,
    _: &Substitution,
) -> Result<Layout, LayoutError> {
    user_error!("infinite sized recursive type");
}