]>
Commit | Line | Data |
---|---|---|
9c376795 FG |
1 | //! Compute the binary representation of structs, unions and enums |
2 | ||
fe692bf9 | 3 | use std::{cmp, ops::Bound}; |
9c376795 | 4 | |
4b012472 | 5 | use base_db::salsa::Cycle; |
9c376795 | 6 | use hir_def::{ |
fe692bf9 FG |
7 | data::adt::VariantData, |
8 | layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout}, | |
9 | AdtId, EnumVariantId, LocalEnumVariantId, VariantId, | |
9c376795 FG |
10 | }; |
11 | use la_arena::RawIdx; | |
4b012472 | 12 | use rustc_dependencies::index::IndexVec; |
9c376795 | 13 | use smallvec::SmallVec; |
fe692bf9 | 14 | use triomphe::Arc; |
9c376795 | 15 | |
fe692bf9 FG |
16 | use 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 | 23 | use super::LayoutCx; |
9c376795 | 24 | |
4b012472 FG |
25 | pub(crate) const fn struct_variant_idx() -> RustcEnumVariantIdx { |
26 | RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from_u32(0))) | |
9c376795 FG |
27 | } |
28 | ||
29 | pub 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 | ||
118 | fn 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 | ||
142 | pub 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. | |
156 | fn 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 | } |