]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/hir-ty/src/layout.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.rs
1 //! Compute the binary representation of a type
2
3 use std::sync::Arc;
4
5 use base_db::CrateId;
6 use chalk_ir::{AdtId, TyKind};
7 use hir_def::{
8     layout::{
9         Abi, FieldsShape, Integer, Layout, LayoutCalculator, LayoutError, Primitive, ReprOptions,
10         RustcEnumVariantIdx, Scalar, Size, StructKind, TargetDataLayout, Variants, WrappingRange,
11     },
12     LocalFieldId,
13 };
14 use stdx::never;
15
16 use crate::{db::HirDatabase, Interner, Substitution, Ty};
17
18 use self::adt::struct_variant_idx;
19 pub use self::{
20     adt::{layout_of_adt_query, layout_of_adt_recover},
21     target::target_data_layout_query,
22 };
23
24 macro_rules! user_error {
25     ($x: expr) => {
26         return Err(LayoutError::UserError(format!($x)))
27     };
28 }
29
30 mod adt;
31 mod target;
32
33 struct LayoutCx<'a> {
34     db: &'a dyn HirDatabase,
35     krate: CrateId,
36 }
37
38 impl LayoutCalculator for LayoutCx<'_> {
39     type TargetDataLayoutRef = Arc<TargetDataLayout>;
40
41     fn delay_bug(&self, txt: &str) {
42         never!("{}", txt);
43     }
44
45     fn current_data_layout(&self) -> Arc<TargetDataLayout> {
46         self.db.target_data_layout(self.krate)
47     }
48 }
49
50 fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
51     Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
52 }
53
54 fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
55     Layout::scalar(dl, scalar_unit(dl, value))
56 }
57
58 pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Layout, LayoutError> {
59     let cx = LayoutCx { db, krate };
60     let dl = &*cx.current_data_layout();
61     Ok(match ty.kind(Interner) {
62         TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
63         TyKind::Scalar(s) => match s {
64             chalk_ir::Scalar::Bool => Layout::scalar(
65                 dl,
66                 Scalar::Initialized {
67                     value: Primitive::Int(Integer::I8, false),
68                     valid_range: WrappingRange { start: 0, end: 1 },
69                 },
70             ),
71             chalk_ir::Scalar::Char => Layout::scalar(
72                 dl,
73                 Scalar::Initialized {
74                     value: Primitive::Int(Integer::I32, false),
75                     valid_range: WrappingRange { start: 0, end: 0x10FFFF },
76                 },
77             ),
78             chalk_ir::Scalar::Int(i) => scalar(
79                 dl,
80                 Primitive::Int(
81                     match i {
82                         chalk_ir::IntTy::Isize => dl.ptr_sized_integer(),
83                         chalk_ir::IntTy::I8 => Integer::I8,
84                         chalk_ir::IntTy::I16 => Integer::I16,
85                         chalk_ir::IntTy::I32 => Integer::I32,
86                         chalk_ir::IntTy::I64 => Integer::I64,
87                         chalk_ir::IntTy::I128 => Integer::I128,
88                     },
89                     true,
90                 ),
91             ),
92             chalk_ir::Scalar::Uint(i) => scalar(
93                 dl,
94                 Primitive::Int(
95                     match i {
96                         chalk_ir::UintTy::Usize => dl.ptr_sized_integer(),
97                         chalk_ir::UintTy::U8 => Integer::I8,
98                         chalk_ir::UintTy::U16 => Integer::I16,
99                         chalk_ir::UintTy::U32 => Integer::I32,
100                         chalk_ir::UintTy::U64 => Integer::I64,
101                         chalk_ir::UintTy::U128 => Integer::I128,
102                     },
103                     false,
104                 ),
105             ),
106             chalk_ir::Scalar::Float(f) => scalar(
107                 dl,
108                 match f {
109                     chalk_ir::FloatTy::F32 => Primitive::F32,
110                     chalk_ir::FloatTy::F64 => Primitive::F64,
111                 },
112             ),
113         },
114         TyKind::Tuple(len, tys) => {
115             let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
116
117             let fields = tys
118                 .iter(Interner)
119                 .map(|k| layout_of_ty(db, k.assert_ty_ref(Interner), krate))
120                 .collect::<Result<Vec<_>, _>>()?;
121             let fields = fields.iter().collect::<Vec<_>>();
122             let fields = fields.iter().collect::<Vec<_>>();
123             cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
124         }
125         TyKind::Array(element, count) => {
126             let count = match count.data(Interner).value {
127                 chalk_ir::ConstValue::Concrete(c) => match c.interned {
128                     hir_def::type_ref::ConstScalar::Int(x) => x as u64,
129                     hir_def::type_ref::ConstScalar::UInt(x) => x as u64,
130                     hir_def::type_ref::ConstScalar::Unknown => {
131                         user_error!("unknown const generic parameter")
132                     }
133                     _ => user_error!("mismatched type of const generic parameter"),
134                 },
135                 _ => return Err(LayoutError::HasPlaceholder),
136             };
137             let element = layout_of_ty(db, element, krate)?;
138             let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
139
140             let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
141                 Abi::Uninhabited
142             } else {
143                 Abi::Aggregate { sized: true }
144             };
145
146             let largest_niche = if count != 0 { element.largest_niche } else { None };
147
148             Layout {
149                 variants: Variants::Single { index: struct_variant_idx() },
150                 fields: FieldsShape::Array { stride: element.size, count },
151                 abi,
152                 largest_niche,
153                 align: element.align,
154                 size,
155             }
156         }
157         TyKind::Slice(element) => {
158             let element = layout_of_ty(db, element, krate)?;
159             Layout {
160                 variants: Variants::Single { index: struct_variant_idx() },
161                 fields: FieldsShape::Array { stride: element.size, count: 0 },
162                 abi: Abi::Aggregate { sized: false },
163                 largest_niche: None,
164                 align: element.align,
165                 size: Size::ZERO,
166             }
167         }
168         // Potentially-wide pointers.
169         TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => {
170             let mut data_ptr = scalar_unit(dl, Primitive::Pointer);
171             if matches!(ty.kind(Interner), TyKind::Ref(..)) {
172                 data_ptr.valid_range_mut().start = 1;
173             }
174
175             // let pointee = tcx.normalize_erasing_regions(param_env, pointee);
176             // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
177             //     return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr)));
178             // }
179
180             let unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
181             let metadata = match unsized_part.kind(Interner) {
182                 TyKind::Slice(_) | TyKind::Str => {
183                     scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
184                 }
185                 TyKind::Dyn(..) => {
186                     let mut vtable = scalar_unit(dl, Primitive::Pointer);
187                     vtable.valid_range_mut().start = 1;
188                     vtable
189                 }
190                 _ => {
191                     // pointee is sized
192                     return Ok(Layout::scalar(dl, data_ptr));
193                 }
194             };
195
196             // Effectively a (ptr, meta) tuple.
197             cx.scalar_pair(data_ptr, metadata)
198         }
199         TyKind::FnDef(_, _) => layout_of_unit(&cx, dl)?,
200         TyKind::Str => Layout {
201             variants: Variants::Single { index: struct_variant_idx() },
202             fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 },
203             abi: Abi::Aggregate { sized: false },
204             largest_niche: None,
205             align: dl.i8_align,
206             size: Size::ZERO,
207         },
208         TyKind::Never => Layout {
209             variants: Variants::Single { index: struct_variant_idx() },
210             fields: FieldsShape::Primitive,
211             abi: Abi::Uninhabited,
212             largest_niche: None,
213             align: dl.i8_align,
214             size: Size::ZERO,
215         },
216         TyKind::Dyn(_) | TyKind::Foreign(_) => {
217             let mut unit = layout_of_unit(&cx, dl)?;
218             match unit.abi {
219                 Abi::Aggregate { ref mut sized } => *sized = false,
220                 _ => user_error!("bug"),
221             }
222             unit
223         }
224         TyKind::Function(_) => {
225             let mut ptr = scalar_unit(dl, Primitive::Pointer);
226             ptr.valid_range_mut().start = 1;
227             Layout::scalar(dl, ptr)
228         }
229         TyKind::Closure(_, _)
230         | TyKind::OpaqueType(_, _)
231         | TyKind::Generator(_, _)
232         | TyKind::GeneratorWitness(_, _) => return Err(LayoutError::NotImplemented),
233         TyKind::AssociatedType(_, _)
234         | TyKind::Error
235         | TyKind::Alias(_)
236         | TyKind::Placeholder(_)
237         | TyKind::BoundVar(_)
238         | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),
239     })
240 }
241
242 fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> {
243     cx.univariant::<RustcEnumVariantIdx, &&Layout>(
244         dl,
245         &[],
246         &ReprOptions::default(),
247         StructKind::AlwaysSized,
248     )
249     .ok_or(LayoutError::Unknown)
250 }
251
252 fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty {
253     match pointee.kind(Interner) {
254         TyKind::Adt(AdtId(adt), subst) => match adt {
255             &hir_def::AdtId::StructId(i) => {
256                 let data = db.struct_data(i);
257                 let mut it = data.variant_data.fields().iter().rev();
258                 match it.next() {
259                     Some((f, _)) => field_ty(db, i.into(), f, subst),
260                     None => pointee,
261                 }
262             }
263             _ => pointee,
264         },
265         _ => pointee,
266     }
267 }
268
269 fn field_ty(
270     db: &dyn HirDatabase,
271     def: hir_def::VariantId,
272     fd: LocalFieldId,
273     subst: &Substitution,
274 ) -> Ty {
275     db.field_types(def)[fd].clone().substitute(Interner, subst)
276 }
277
278 #[cfg(test)]
279 mod tests;