]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir-ty / src / layout / adt.rs
CommitLineData
9c376795
FG
1//! Compute the binary representation of structs, unions and enums
2
fe692bf9 3use std::{cmp, ops::Bound};
9c376795 4
4b012472 5use base_db::salsa::Cycle;
9c376795 6use hir_def::{
fe692bf9
FG
7 data::adt::VariantData,
8 layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout},
9 AdtId, EnumVariantId, LocalEnumVariantId, VariantId,
9c376795
FG
10};
11use la_arena::RawIdx;
4b012472 12use rustc_dependencies::index::IndexVec;
9c376795 13use smallvec::SmallVec;
fe692bf9 14use triomphe::Arc;
9c376795 15
fe692bf9
FG
16use crate::{
17 db::HirDatabase,
18 lang_items::is_unsafe_cell,
19 layout::{field_ty, Layout, LayoutError, RustcEnumVariantIdx},
add651ee 20 Substitution, TraitEnvironment,
fe692bf9 21};
9c376795 22
fe692bf9 23use super::LayoutCx;
9c376795 24
4b012472
FG
25pub(crate) const fn struct_variant_idx() -> RustcEnumVariantIdx {
26 RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from_u32(0)))
9c376795
FG
27}
28
29pub fn layout_of_adt_query(
30 db: &dyn HirDatabase,
31 def: AdtId,
32 subst: Substitution,
add651ee 33 trait_env: Arc<TraitEnvironment>,
fe692bf9 34) -> Result<Arc<Layout>, LayoutError> {
add651ee
FG
35 let krate = trait_env.krate;
36 let Some(target) = db.target_data_layout(krate) else {
37 return Err(LayoutError::TargetLayoutNotAvailable);
38 };
39 let cx = LayoutCx { target: &target };
9c376795
FG
40 let dl = cx.current_data_layout();
41 let handle_variant = |def: VariantId, var: &VariantData| {
42 var.fields()
43 .iter()
add651ee 44 .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), trait_env.clone()))
9c376795
FG
45 .collect::<Result<Vec<_>, _>>()
46 };
fe692bf9 47 let (variants, repr) = match def {
9c376795
FG
48 AdtId::StructId(s) => {
49 let data = db.struct_data(s);
50 let mut r = SmallVec::<[_; 1]>::new();
51 r.push(handle_variant(s.into(), &data.variant_data)?);
fe692bf9 52 (r, data.repr.unwrap_or_default())
9c376795
FG
53 }
54 AdtId::UnionId(id) => {
55 let data = db.union_data(id);
56 let mut r = SmallVec::new();
57 r.push(handle_variant(id.into(), &data.variant_data)?);
fe692bf9 58 (r, data.repr.unwrap_or_default())
9c376795
FG
59 }
60 AdtId::EnumId(e) => {
61 let data = db.enum_data(e);
62 let r = data
63 .variants
64 .iter()
65 .map(|(idx, v)| {
66 handle_variant(
67 EnumVariantId { parent: e, local_id: idx }.into(),
68 &v.variant_data,
69 )
70 })
71 .collect::<Result<SmallVec<_>, _>>()?;
fe692bf9 72 (r, data.repr.unwrap_or_default())
9c376795
FG
73 }
74 };
fe692bf9
FG
75 let variants = variants
76 .iter()
add651ee 77 .map(|it| it.iter().map(|it| &**it).collect::<Vec<_>>())
fe692bf9 78 .collect::<SmallVec<[_; 1]>>();
4b012472 79 let variants = variants.iter().map(|it| it.iter().collect()).collect::<IndexVec<_, _>>();
fe692bf9
FG
80 let result = if matches!(def, AdtId::UnionId(..)) {
81 cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)?
9c376795
FG
82 } else {
83 cx.layout_of_struct_or_enum(
84 &repr,
85 &variants,
fe692bf9
FG
86 matches!(def, AdtId::EnumId(..)),
87 is_unsafe_cell(db, def),
9c376795 88 layout_scalar_valid_range(db, def),
fe692bf9 89 |min, max| repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
9c376795
FG
90 variants.iter_enumerated().filter_map(|(id, _)| {
91 let AdtId::EnumId(e) = def else { return None };
353b0b11
FG
92 let d =
93 db.const_eval_discriminant(EnumVariantId { parent: e, local_id: id.0 }).ok()?;
9c376795
FG
94 Some((id, d))
95 }),
96 // FIXME: The current code for niche-filling relies on variant indices
97 // instead of actual discriminants, so enums with
98 // explicit discriminants (RFC #2363) would misbehave and we should disable
99 // niche optimization for them.
100 // The code that do it in rustc:
101 // repr.inhibit_enum_layout_opt() || def
102 // .variants()
103 // .iter_enumerated()
104 // .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
105 repr.inhibit_enum_layout_opt(),
fe692bf9 106 !matches!(def, AdtId::EnumId(..))
9c376795
FG
107 && variants
108 .iter()
109 .next()
4b012472 110 .and_then(|it| it.iter().last().map(|it| !it.is_unsized()))
9c376795
FG
111 .unwrap_or(true),
112 )
fe692bf9
FG
113 .ok_or(LayoutError::SizeOverflow)?
114 };
115 Ok(Arc::new(result))
9c376795
FG
116}
117
118fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
119 let attrs = db.attrs(def.into());
120 let get = |name| {
121 let attr = attrs.by_key(name).tt_values();
122 for tree in attr {
add651ee 123 if let Some(it) = tree.token_trees.first() {
4b012472
FG
124 let text = it.to_string().replace('_', "");
125 let (text, base) = match text.as_bytes() {
126 [b'0', b'x', ..] => (&text[2..], 16),
127 [b'0', b'o', ..] => (&text[2..], 8),
128 [b'0', b'b', ..] => (&text[2..], 2),
129 _ => (&*text, 10),
130 };
131
132 if let Ok(it) = u128::from_str_radix(text, base) {
add651ee 133 return Bound::Included(it);
9c376795
FG
134 }
135 }
136 }
137 Bound::Unbounded
138 };
139 (get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end"))
140}
141
142pub fn layout_of_adt_recover(
143 _: &dyn HirDatabase,
4b012472 144 _: &Cycle,
9c376795
FG
145 _: &AdtId,
146 _: &Substitution,
add651ee 147 _: &Arc<TraitEnvironment>,
fe692bf9 148) -> Result<Arc<Layout>, LayoutError> {
4b012472 149 Err(LayoutError::RecursiveTypeWithoutIndirection)
9c376795 150}
fe692bf9
FG
151
152/// Finds the appropriate Integer type and signedness for the given
153/// signed discriminant range and `#[repr]` attribute.
154/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
155/// that shouldn't affect anything, other than maybe debuginfo.
156fn repr_discr(
157 dl: &TargetDataLayout,
158 repr: &ReprOptions,
159 min: i128,
160 max: i128,
161) -> Result<(Integer, bool), LayoutError> {
162 // Theoretically, negative values could be larger in unsigned representation
163 // than the unsigned representation of the signed minimum. However, if there
164 // are any negative values, the only valid unsigned representation is u128
165 // which can fit all i128 values, so the result remains unaffected.
166 let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
167 let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
168
169 if let Some(ity) = repr.int {
170 let discr = Integer::from_attr(dl, ity);
171 let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
172 if discr < fit {
4b012472 173 return Err(LayoutError::UserReprTooSmall);
fe692bf9
FG
174 }
175 return Ok((discr, ity.is_signed()));
176 }
177
178 let at_least = if repr.c() {
179 // This is usually I32, however it can be different on some platforms,
180 // notably hexagon and arm-none/thumb-none
181 dl.c_enum_min_size
182 } else {
183 // repr(Rust) enums try to be as small as possible
184 Integer::I8
185 };
186
187 // If there are no negative values, we can use the unsigned fit.
188 Ok(if min >= 0 {
189 (cmp::max(unsigned_fit, at_least), false)
190 } else {
191 (cmp::max(signed_fit, at_least), true)
192 })
193}