1use 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
110fn repr_discr(
115 dl: &TargetDataLayout,
116 repr: &ReprOptions,
117 min: i128,
118 max: i128,
119) -> Result<(Integer, bool), LayoutError> {
120 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 dl.c_enum_min_size
140 } else {
141 Integer::I8
143 };
144
145 Ok(if min >= 0 {
147 (cmp::max(unsigned_fit, at_least), false)
148 } else {
149 (cmp::max(signed_fit, at_least), true)
150 })
151}