1use std::{cmp, ops::Bound};
4
5use hir_def::{
6 AdtId, VariantId,
7 signatures::{StructFlags, VariantFields},
8};
9use intern::sym;
10use rustc_abi::{Integer, ReprOptions, TargetDataLayout};
11use rustc_index::IndexVec;
12use smallvec::SmallVec;
13use triomphe::Arc;
14
15use crate::{
16 TraitEnvironment,
17 db::HirDatabase,
18 layout::{Layout, LayoutCx, LayoutError, field_ty},
19 next_solver::GenericArgs,
20};
21
22pub fn layout_of_adt_query<'db>(
23 db: &'db dyn HirDatabase,
24 def: AdtId,
25 args: GenericArgs<'db>,
26 trait_env: Arc<TraitEnvironment>,
27) -> Result<Arc<Layout>, LayoutError> {
28 let krate = trait_env.krate;
29 let Ok(target) = db.target_data_layout(krate) else {
30 return Err(LayoutError::TargetLayoutNotAvailable);
31 };
32 let dl = &*target;
33 let cx = LayoutCx::new(dl);
34 let handle_variant = |def: VariantId, var: &VariantFields| {
35 var.fields()
36 .iter()
37 .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &args), trait_env.clone()))
38 .collect::<Result<Vec<_>, _>>()
39 };
40 let (variants, repr, is_special_no_niche) = match def {
41 AdtId::StructId(s) => {
42 let sig = db.struct_signature(s);
43 let mut r = SmallVec::<[_; 1]>::new();
44 r.push(handle_variant(s.into(), s.fields(db))?);
45 (
46 r,
47 sig.repr.unwrap_or_default(),
48 sig.flags.intersects(StructFlags::IS_UNSAFE_CELL | StructFlags::IS_UNSAFE_PINNED),
49 )
50 }
51 AdtId::UnionId(id) => {
52 let data = db.union_signature(id);
53 let mut r = SmallVec::new();
54 r.push(handle_variant(id.into(), id.fields(db))?);
55 (r, data.repr.unwrap_or_default(), false)
56 }
57 AdtId::EnumId(e) => {
58 let variants = e.enum_variants(db);
59 let r = variants
60 .variants
61 .iter()
62 .map(|&(v, _, _)| handle_variant(v.into(), v.fields(db)))
63 .collect::<Result<SmallVec<_>, _>>()?;
64 (r, db.enum_signature(e).repr.unwrap_or_default(), false)
65 }
66 };
67 let variants = variants
68 .iter()
69 .map(|it| it.iter().map(|it| &**it).collect::<Vec<_>>())
70 .collect::<SmallVec<[_; 1]>>();
71 let variants = variants.iter().map(|it| it.iter().collect()).collect::<IndexVec<_, _>>();
72 let result = if matches!(def, AdtId::UnionId(..)) {
73 cx.calc.layout_of_union(&repr, &variants)?
74 } else {
75 cx.calc.layout_of_struct_or_enum(
76 &repr,
77 &variants,
78 matches!(def, AdtId::EnumId(..)),
79 is_special_no_niche,
80 layout_scalar_valid_range(db, def),
81 |min, max| repr_discr(dl, &repr, min, max).unwrap_or((Integer::I8, false)),
82 variants.iter_enumerated().filter_map(|(id, _)| {
83 let AdtId::EnumId(e) = def else { return None };
84 let d = db.const_eval_discriminant(e.enum_variants(db).variants[id.0].0).ok()?;
85 Some((id, d))
86 }),
87 !matches!(def, AdtId::EnumId(..))
88 && variants
89 .iter()
90 .next()
91 .and_then(|it| it.iter().last().map(|it| !it.is_unsized()))
92 .unwrap_or(true),
93 )?
94 };
95 Ok(Arc::new(result))
96}
97
98pub(crate) fn layout_of_adt_cycle_result<'db>(
99 _: &'db dyn HirDatabase,
100 _def: AdtId,
101 _args: GenericArgs<'db>,
102 _trait_env: Arc<TraitEnvironment>,
103) -> Result<Arc<Layout>, LayoutError> {
104 Err(LayoutError::RecursiveTypeWithoutIndirection)
105}
106
107fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
108 let attrs = db.attrs(def.into());
109 let get = |name| {
110 let attr = attrs.by_key(name).tt_values();
111 for tree in attr {
112 if let Some(it) = tree.iter().next_as_view() {
113 let text = it.to_string().replace('_', "");
114 let (text, base) = match text.as_bytes() {
115 [b'0', b'x', ..] => (&text[2..], 16),
116 [b'0', b'o', ..] => (&text[2..], 8),
117 [b'0', b'b', ..] => (&text[2..], 2),
118 _ => (&*text, 10),
119 };
120
121 if let Ok(it) = u128::from_str_radix(text, base) {
122 return Bound::Included(it);
123 }
124 }
125 }
126 Bound::Unbounded
127 };
128 (get(sym::rustc_layout_scalar_valid_range_start), get(sym::rustc_layout_scalar_valid_range_end))
129}
130
131fn repr_discr(
136 dl: &TargetDataLayout,
137 repr: &ReprOptions,
138 min: i128,
139 max: i128,
140) -> Result<(Integer, bool), LayoutError> {
141 let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
146 let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
147
148 if let Some(ity) = repr.int {
149 let discr = Integer::from_attr(dl, ity);
150 let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
151 if discr < fit {
152 return Err(LayoutError::UserReprTooSmall);
153 }
154 return Ok((discr, ity.is_signed()));
155 }
156
157 let at_least = if repr.c() {
158 dl.c_enum_min_size
161 } else {
162 Integer::I8
164 };
165
166 Ok(if min >= 0 {
168 (cmp::max(unsigned_fit, at_least), false)
169 } else {
170 (cmp::max(signed_fit, at_least), true)
171 })
172}