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