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