1use std::fmt;
4
5use hir_def::{
6 AdtId, LocalFieldId, StructId,
7 attrs::AttrFlags,
8 layout::{LayoutCalculatorError, LayoutData},
9};
10use la_arena::{Idx, RawIdx};
11
12use rustc_abi::{
13 AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind,
14 TargetDataLayout, WrappingRange,
15};
16use rustc_index::IndexVec;
17use rustc_type_ir::{
18 FloatTy, IntTy, UintTy,
19 inherent::{IntoKind, SliceLike},
20};
21use triomphe::Arc;
22
23use crate::{
24 InferenceResult, ParamEnvAndCrate,
25 consteval::try_const_usize,
26 db::HirDatabase,
27 next_solver::{
28 DbInterner, GenericArgs, Ty, TyKind, TypingMode,
29 infer::{DbInternerInferExt, traits::ObligationCause},
30 },
31};
32
33pub(crate) use self::adt::layout_of_adt_cycle_result;
34pub use self::{adt::layout_of_adt_query, target::target_data_layout_query};
35
36pub(crate) mod adt;
37pub(crate) mod target;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub struct RustcEnumVariantIdx(pub usize);
41
42impl rustc_index::Idx for RustcEnumVariantIdx {
43 fn new(idx: usize) -> Self {
44 RustcEnumVariantIdx(idx)
45 }
46
47 fn index(self) -> usize {
48 self.0
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub struct RustcFieldIdx(pub LocalFieldId);
54
55impl RustcFieldIdx {
56 pub fn new(idx: usize) -> Self {
57 RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
58 }
59}
60
61impl rustc_index::Idx for RustcFieldIdx {
62 fn new(idx: usize) -> Self {
63 RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
64 }
65
66 fn index(self) -> usize {
67 u32::from(self.0.into_raw()) as usize
68 }
69}
70
71pub type Layout = LayoutData<RustcFieldIdx, RustcEnumVariantIdx>;
72pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>;
73pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx>;
74
75#[derive(Debug, PartialEq, Eq, Clone)]
76pub enum LayoutError {
77 BadCalc(LayoutCalculatorError<()>),
79 HasErrorConst,
80 HasErrorType,
81 HasPlaceholder,
82 InvalidSimdType,
83 NotImplemented,
84 RecursiveTypeWithoutIndirection,
85 TargetLayoutNotAvailable,
86 Unknown,
87 UserReprTooSmall,
88}
89
90impl std::error::Error for LayoutError {}
91impl fmt::Display for LayoutError {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 LayoutError::BadCalc(err) => err.fallback_fmt(f),
95 LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"),
96 LayoutError::HasErrorType => write!(f, "type contains an error"),
97 LayoutError::HasPlaceholder => write!(f, "type contains placeholders"),
98 LayoutError::InvalidSimdType => write!(f, "invalid simd type definition"),
99 LayoutError::NotImplemented => write!(f, "not implemented"),
100 LayoutError::RecursiveTypeWithoutIndirection => {
101 write!(f, "recursive type without indirection")
102 }
103 LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"),
104 LayoutError::Unknown => write!(f, "unknown"),
105 LayoutError::UserReprTooSmall => {
106 write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum")
107 }
108 }
109 }
110}
111
112impl<F> From<LayoutCalculatorError<F>> for LayoutError {
113 fn from(err: LayoutCalculatorError<F>) -> Self {
114 LayoutError::BadCalc(err.without_payload())
115 }
116}
117
118struct LayoutCx<'a> {
119 calc: LayoutCalculator<&'a TargetDataLayout>,
120}
121
122impl<'a> LayoutCx<'a> {
123 fn new(target: &'a TargetDataLayout) -> Self {
124 Self { calc: LayoutCalculator::new(target) }
125 }
126}
127
128fn layout_of_simd_ty<'db>(
130 db: &'db dyn HirDatabase,
131 id: StructId,
132 repr_packed: bool,
133 args: &GenericArgs<'db>,
134 env: ParamEnvAndCrate<'db>,
135 dl: &TargetDataLayout,
136) -> Result<Arc<Layout>, LayoutError> {
137 let fields = db.field_types(id.into());
143 let mut fields = fields.iter();
144 let Some(TyKind::Array(e_ty, e_len)) = fields
145 .next()
146 .filter(|_| fields.next().is_none())
147 .map(|f| (*f.1).instantiate(DbInterner::new_no_crate(db), args).kind())
148 else {
149 return Err(LayoutError::InvalidSimdType);
150 };
151
152 let e_len = try_const_usize(db, e_len).ok_or(LayoutError::HasErrorConst)? as u64;
153 let e_ly = db.layout_of_ty(e_ty, env)?;
154
155 let cx = LayoutCx::new(dl);
156 Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
157}
158
159pub fn layout_of_ty_query<'db>(
160 db: &'db dyn HirDatabase,
161 ty: Ty<'db>,
162 trait_env: ParamEnvAndCrate<'db>,
163) -> Result<Arc<Layout>, LayoutError> {
164 let krate = trait_env.krate;
165 let interner = DbInterner::new_with(db, krate);
166 let Ok(target) = db.target_data_layout(krate) else {
167 return Err(LayoutError::TargetLayoutNotAvailable);
168 };
169 let dl = &*target;
170 let cx = LayoutCx::new(dl);
171 let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
172 let cause = ObligationCause::dummy();
173 let ty = infer_ctxt.at(&cause, trait_env.param_env).deeply_normalize(ty).unwrap_or(ty);
174 let result = match ty.kind() {
175 TyKind::Adt(def, args) => {
176 match def.inner().id {
177 hir_def::AdtId::StructId(s) => {
178 let repr = AttrFlags::repr(db, s.into()).unwrap_or_default();
179 if repr.simd() {
180 return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &target);
181 }
182 }
183 _ => {}
184 }
185 return db.layout_of_adt(def.inner().id, args, trait_env);
186 }
187 TyKind::Bool => Layout::scalar(
188 dl,
189 Scalar::Initialized {
190 value: Primitive::Int(Integer::I8, false),
191 valid_range: WrappingRange { start: 0, end: 1 },
192 },
193 ),
194 TyKind::Char => Layout::scalar(
195 dl,
196 Scalar::Initialized {
197 value: Primitive::Int(Integer::I32, false),
198 valid_range: WrappingRange { start: 0, end: 0x10FFFF },
199 },
200 ),
201 TyKind::Int(i) => Layout::scalar(
202 dl,
203 scalar_unit(
204 dl,
205 Primitive::Int(
206 match i {
207 IntTy::Isize => dl.ptr_sized_integer(),
208 IntTy::I8 => Integer::I8,
209 IntTy::I16 => Integer::I16,
210 IntTy::I32 => Integer::I32,
211 IntTy::I64 => Integer::I64,
212 IntTy::I128 => Integer::I128,
213 },
214 true,
215 ),
216 ),
217 ),
218 TyKind::Uint(i) => Layout::scalar(
219 dl,
220 scalar_unit(
221 dl,
222 Primitive::Int(
223 match i {
224 UintTy::Usize => dl.ptr_sized_integer(),
225 UintTy::U8 => Integer::I8,
226 UintTy::U16 => Integer::I16,
227 UintTy::U32 => Integer::I32,
228 UintTy::U64 => Integer::I64,
229 UintTy::U128 => Integer::I128,
230 },
231 false,
232 ),
233 ),
234 ),
235 TyKind::Float(f) => Layout::scalar(
236 dl,
237 scalar_unit(
238 dl,
239 Primitive::Float(match f {
240 FloatTy::F16 => Float::F16,
241 FloatTy::F32 => Float::F32,
242 FloatTy::F64 => Float::F64,
243 FloatTy::F128 => Float::F128,
244 }),
245 ),
246 ),
247 TyKind::Tuple(tys) => {
248 let kind =
249 if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
250
251 let fields =
252 tys.iter().map(|k| db.layout_of_ty(k, trait_env)).collect::<Result<Vec<_>, _>>()?;
253 let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
254 let fields = fields.iter().collect::<IndexVec<_, _>>();
255 cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
256 }
257 TyKind::Array(element, count) => {
258 let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
259 let element = db.layout_of_ty(element, trait_env)?;
260 cx.calc.array_like::<_, _, ()>(&element, Some(count))?
261 }
262 TyKind::Slice(element) => {
263 let element = db.layout_of_ty(element, trait_env)?;
264 cx.calc.array_like::<_, _, ()>(&element, None)?
265 }
266 TyKind::Str => {
267 let element = scalar_unit(dl, Primitive::Int(Integer::I8, false));
268 cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)?
269 }
270 TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => {
272 let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
273 if matches!(ty.kind(), TyKind::Ref(..)) {
274 data_ptr.valid_range_mut().start = 1;
275 }
276
277 let unsized_part = struct_tail_erasing_lifetimes(db, pointee);
284 let metadata = match unsized_part.kind() {
296 TyKind::Slice(_) | TyKind::Str => {
297 scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
298 }
299 TyKind::Dynamic(..) => {
300 let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
301 vtable.valid_range_mut().start = 1;
302 vtable
303 }
304 _ => {
305 return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
307 }
308 };
309
310 LayoutData::scalar_pair(dl, data_ptr, metadata)
312 }
313 TyKind::Never => LayoutData::never_type(dl),
314 TyKind::FnDef(..) => LayoutData::unit(dl, true),
315 TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false),
316 TyKind::FnPtr(..) => {
317 let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space));
318 ptr.valid_range_mut().start = 1;
319 Layout::scalar(dl, ptr)
320 }
321 TyKind::Closure(id, args) => {
322 let def = db.lookup_intern_closure(id.0);
323 let infer = InferenceResult::for_body(db, def.0);
324 let (captures, _) = infer.closure_info(id.0);
325 let fields = captures
326 .iter()
327 .map(|it| {
328 let ty =
329 it.ty.instantiate(interner, args.split_closure_args_untupled().parent_args);
330 db.layout_of_ty(ty, trait_env)
331 })
332 .collect::<Result<Vec<_>, _>>()?;
333 let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
334 let fields = fields.iter().collect::<IndexVec<_, _>>();
335 cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)?
336 }
337
338 TyKind::Coroutine(_, _)
339 | TyKind::CoroutineWitness(_, _)
340 | TyKind::CoroutineClosure(_, _) => {
341 return Err(LayoutError::NotImplemented);
342 }
343
344 TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => {
345 return Err(LayoutError::NotImplemented);
346 }
347
348 TyKind::Error(_) => return Err(LayoutError::HasErrorType),
349 TyKind::Placeholder(_)
350 | TyKind::Bound(..)
351 | TyKind::Infer(..)
352 | TyKind::Param(..)
353 | TyKind::Alias(..) => {
354 return Err(LayoutError::HasPlaceholder);
355 }
356 };
357 Ok(Arc::new(result))
358}
359
360pub(crate) fn layout_of_ty_cycle_result<'db>(
361 _: &dyn HirDatabase,
362 _: Ty<'db>,
363 _: ParamEnvAndCrate<'db>,
364) -> Result<Arc<Layout>, LayoutError> {
365 Err(LayoutError::RecursiveTypeWithoutIndirection)
366}
367
368fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
369 match pointee.kind() {
370 TyKind::Adt(def, args) => {
371 let struct_id = match def.inner().id {
372 AdtId::StructId(id) => id,
373 _ => return pointee,
374 };
375 let data = struct_id.fields(db);
376 let mut it = data.fields().iter().rev();
377 match it.next() {
378 Some((f, _)) => {
379 let last_field_ty = field_ty(db, struct_id.into(), f, &args);
380 struct_tail_erasing_lifetimes(db, last_field_ty)
381 }
382 None => pointee,
383 }
384 }
385 TyKind::Tuple(tys) => {
386 if let Some(last_field_ty) = tys.iter().next_back() {
387 struct_tail_erasing_lifetimes(db, last_field_ty)
388 } else {
389 pointee
390 }
391 }
392 _ => pointee,
393 }
394}
395
396fn field_ty<'a>(
397 db: &'a dyn HirDatabase,
398 def: hir_def::VariantId,
399 fd: LocalFieldId,
400 args: &GenericArgs<'a>,
401) -> Ty<'a> {
402 db.field_types(def)[fd].instantiate(DbInterner::new_no_crate(db), args)
403}
404
405fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
406 Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
407}
408
409#[cfg(test)]
410mod tests;