]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / src / tools / rust-analyzer / crates / hir-ty / src / layout / adt.rs
1 //! Compute the binary representation of structs, unions and enums
2
3 use std::ops::Bound;
4
5 use hir_def::{
6     adt::VariantData,
7     layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
8     AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
9 };
10 use la_arena::RawIdx;
11 use smallvec::SmallVec;
12
13 use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution};
14
15 use super::{layout_of_ty, LayoutCx};
16
17 pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
18     RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
19 }
20
21 pub fn layout_of_adt_query(
22     db: &dyn HirDatabase,
23     def: AdtId,
24     subst: Substitution,
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| {
29         var.fields()
30             .iter()
31             .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
32             .collect::<Result<Vec<_>, _>>()
33     };
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())
40         }
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())
46         }
47         AdtId::EnumId(e) => {
48             let data = db.enum_data(e);
49             let r = data
50                 .variants
51                 .iter()
52                 .map(|(idx, v)| {
53                     handle_variant(
54                         EnumVariantId { parent: e, local_id: idx }.into(),
55                         &v.variant_data,
56                     )
57                 })
58                 .collect::<Result<SmallVec<_>, _>>()?;
59             (r, true, false, data.repr.unwrap_or_default())
60         }
61     };
62     let variants =
63         variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>();
64     let variants = variants.iter().map(|x| x.iter().collect()).collect();
65     if is_union {
66         cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
67     } else {
68         cx.layout_of_struct_or_enum(
69             &repr,
70             &variants,
71             is_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 };
77                 let d = match db
78                     .const_eval_variant(EnumVariantId { parent: e, local_id: id.0 })
79                     .ok()?
80                 {
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,
84                         _ => return None,
85                     },
86                     _ => return None,
87                 };
88                 Some((id, d))
89             }),
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
96             //     .variants()
97             //     .iter_enumerated()
98             //     .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
99             repr.inhibit_enum_layout_opt(),
100             !is_enum
101                 && variants
102                     .iter()
103                     .next()
104                     .and_then(|x| x.last().map(|x| x.is_unsized()))
105                     .unwrap_or(true),
106         )
107         .ok_or(LayoutError::SizeOverflow)
108     }
109 }
110
111 fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
112     let attrs = db.attrs(def.into());
113     let get = |name| {
114         let attr = attrs.by_key(name).tt_values();
115         for tree in attr {
116             if let Some(x) = tree.token_trees.first() {
117                 if let Ok(x) = x.to_string().parse() {
118                     return Bound::Included(x);
119                 }
120             }
121         }
122         Bound::Unbounded
123     };
124     (get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end"))
125 }
126
127 pub fn layout_of_adt_recover(
128     _: &dyn HirDatabase,
129     _: &[String],
130     _: &AdtId,
131     _: &Substitution,
132 ) -> Result<Layout, LayoutError> {
133     user_error!("infinite sized recursive type");
134 }