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 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
114fn repr_discr(
119 dl: &TargetDataLayout,
120 repr: &ReprOptions,
121 min: i128,
122 max: i128,
123) -> Result<(Integer, bool), LayoutError> {
124 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 dl.c_enum_min_size
144 } else {
145 Integer::I8
147 };
148
149 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}