Skip to main content

hir_ty/layout/
adt.rs

1//! Compute the binary representation of structs, unions and enums
2
3use std::cmp;
4
5use hir_def::{
6    AdtId, VariantId,
7    attrs::AttrFlags,
8    signatures::{StructFlags, StructSignature, VariantFields},
9};
10use rustc_abi::{Integer, ReprOptions, TargetDataLayout};
11use rustc_index::IndexVec;
12use smallvec::SmallVec;
13use triomphe::Arc;
14
15use crate::{
16    db::HirDatabase,
17    layout::{Layout, LayoutCx, LayoutError, field_ty},
18    next_solver::StoredGenericArgs,
19    traits::StoredParamEnvAndCrate,
20};
21
22#[salsa::tracked(cycle_result = layout_of_adt_cycle_result)]
23pub fn layout_of_adt_query(
24    db: &dyn HirDatabase,
25    def: AdtId,
26    args: StoredGenericArgs,
27    trait_env: StoredParamEnvAndCrate,
28) -> Result<Arc<Layout>, LayoutError> {
29    let krate = trait_env.krate;
30    let Ok(target) = db.target_data_layout(krate) else {
31        return Err(LayoutError::TargetLayoutNotAvailable);
32    };
33    let dl = target;
34    let cx = LayoutCx::new(dl);
35    let handle_variant = |def: VariantId, var: &VariantFields| {
36        var.fields()
37            .iter()
38            .map(|(fd, _)| {
39                db.layout_of_ty(field_ty(db, def, fd, args.as_ref()).store(), trait_env.clone())
40            })
41            .collect::<Result<Vec<_>, _>>()
42    };
43    let (variants, repr, is_special_no_niche) = match def {
44        AdtId::StructId(s) => {
45            let sig = StructSignature::of(db, s);
46            let mut r = SmallVec::<[_; 1]>::new();
47            r.push(handle_variant(s.into(), s.fields(db))?);
48            (
49                r,
50                AttrFlags::repr(db, s.into()).unwrap_or_default(),
51                sig.flags.intersects(StructFlags::IS_UNSAFE_CELL | StructFlags::IS_UNSAFE_PINNED),
52            )
53        }
54        AdtId::UnionId(id) => {
55            let repr = AttrFlags::repr(db, id.into());
56            let mut r = SmallVec::new();
57            r.push(handle_variant(id.into(), id.fields(db))?);
58            (r, repr.unwrap_or_default(), false)
59        }
60        AdtId::EnumId(e) => {
61            let variants = e.enum_variants(db);
62            let r = variants
63                .variants
64                .values()
65                .map(|&(v, _)| handle_variant(v.into(), v.fields(db)))
66                .collect::<Result<SmallVec<_>, _>>()?;
67            (r, AttrFlags::repr(db, e.into()).unwrap_or_default(), false)
68        }
69    };
70    let variants = variants
71        .iter()
72        .map(|it| it.iter().map(|it| &**it).collect::<Vec<_>>())
73        .collect::<SmallVec<[_; 1]>>();
74    let variants = variants.iter().map(|it| it.iter().collect()).collect::<IndexVec<_, _>>();
75    let result = if matches!(def, AdtId::UnionId(..)) {
76        cx.calc.layout_of_union(&repr, &variants)?
77    } else {
78        cx.calc.layout_of_struct_or_enum(
79            &repr,
80            &variants,
81            matches!(def, AdtId::EnumId(..)),
82            is_special_no_niche,
83            |min, max| repr_discr(dl, &repr, min, max).unwrap_or((Integer::I8, false)),
84            variants.iter_enumerated().filter_map(|(id, _)| {
85                let AdtId::EnumId(e) = def else { return None };
86                let d = db.const_eval_discriminant(e.enum_variants(db).variants[id.0].0).ok()?;
87                Some((id, d))
88            }),
89            !matches!(def, AdtId::EnumId(..))
90                && variants
91                    .iter()
92                    .next()
93                    .and_then(|it| it.iter().last().map(|it| !it.is_unsized()))
94                    .unwrap_or(true),
95        )?
96    };
97    Ok(Arc::new(result))
98}
99
100fn layout_of_adt_cycle_result(
101    _: &dyn HirDatabase,
102    _: salsa::Id,
103    _def: AdtId,
104    _args: StoredGenericArgs,
105    _trait_env: StoredParamEnvAndCrate,
106) -> Result<Arc<Layout>, LayoutError> {
107    Err(LayoutError::RecursiveTypeWithoutIndirection)
108}
109
110/// Finds the appropriate Integer type and signedness for the given
111/// signed discriminant range and `#[repr]` attribute.
112/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
113/// that shouldn't affect anything, other than maybe debuginfo.
114fn repr_discr(
115    dl: &TargetDataLayout,
116    repr: &ReprOptions,
117    min: i128,
118    max: i128,
119) -> Result<(Integer, bool), LayoutError> {
120    // Theoretically, negative values could be larger in unsigned representation
121    // than the unsigned representation of the signed minimum. However, if there
122    // are any negative values, the only valid unsigned representation is u128
123    // which can fit all i128 values, so the result remains unaffected.
124    let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
125    let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
126
127    if let Some(ity) = repr.int {
128        let discr = Integer::from_attr(dl, ity);
129        let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
130        if discr < fit {
131            return Err(LayoutError::UserReprTooSmall);
132        }
133        return Ok((discr, ity.is_signed()));
134    }
135
136    let at_least = if repr.c() {
137        // This is usually I32, however it can be different on some platforms,
138        // notably hexagon and arm-none/thumb-none
139        dl.c_enum_min_size
140    } else {
141        // repr(Rust) enums try to be as small as possible
142        Integer::I8
143    };
144
145    // If there are no negative values, we can use the unsigned fit.
146    Ok(if min >= 0 {
147        (cmp::max(unsigned_fit, at_least), false)
148    } else {
149        (cmp::max(signed_fit, at_least), true)
150    })
151}