1 //! Compute the binary representation of structs, unions and enums
7 layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
8 AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
11 use smallvec::SmallVec;
13 use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution};
15 use super::{layout_of_ty, LayoutCx};
17 pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
18 RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
21 pub fn layout_of_adt_query(
25 ) -> Result<Layout, LayoutError> {
26 let cx = LayoutCx { db, krate: def.module(db.upcast()).krate() };
27 let dl = cx.current_data_layout();
28 let handle_variant = |def: VariantId, var: &VariantData| {
31 .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
32 .collect::<Result<Vec<_>, _>>()
34 let (variants, is_enum, is_union, repr) = match def {
35 AdtId::StructId(s) => {
36 let data = db.struct_data(s);
37 let mut r = SmallVec::<[_; 1]>::new();
38 r.push(handle_variant(s.into(), &data.variant_data)?);
39 (r, false, false, data.repr.unwrap_or_default())
41 AdtId::UnionId(id) => {
42 let data = db.union_data(id);
43 let mut r = SmallVec::new();
44 r.push(handle_variant(id.into(), &data.variant_data)?);
45 (r, false, true, data.repr.unwrap_or_default())
48 let data = db.enum_data(e);
54 EnumVariantId { parent: e, local_id: idx }.into(),
58 .collect::<Result<SmallVec<_>, _>>()?;
59 (r, true, false, data.repr.unwrap_or_default())
63 variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>();
64 let variants = variants.iter().map(|x| x.iter().collect()).collect();
66 cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
68 cx.layout_of_struct_or_enum(
72 is_unsafe_cell(def, db),
73 layout_scalar_valid_range(db, def),
74 |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
75 variants.iter_enumerated().filter_map(|(id, _)| {
76 let AdtId::EnumId(e) = def else { return None };
78 .const_eval_variant(EnumVariantId { parent: e, local_id: id.0 })
81 crate::consteval::ComputedExpr::Literal(l) => match l {
82 hir_def::expr::Literal::Int(i, _) => i,
83 hir_def::expr::Literal::Uint(i, _) => i as i128,
90 // FIXME: The current code for niche-filling relies on variant indices
91 // instead of actual discriminants, so enums with
92 // explicit discriminants (RFC #2363) would misbehave and we should disable
93 // niche optimization for them.
94 // The code that do it in rustc:
95 // repr.inhibit_enum_layout_opt() || def
98 // .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
99 repr.inhibit_enum_layout_opt(),
104 .and_then(|x| x.last().map(|x| x.is_unsized()))
107 .ok_or(LayoutError::SizeOverflow)
111 fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
112 let attrs = db.attrs(def.into());
114 let attr = attrs.by_key(name).tt_values();
116 if let Some(x) = tree.token_trees.first() {
117 if let Ok(x) = x.to_string().parse() {
118 return Bound::Included(x);
124 (get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end"))
127 pub fn layout_of_adt_recover(
132 ) -> Result<Layout, LayoutError> {
133 user_error!("infinite sized recursive type");