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, BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, Niche, Primitive,
14 ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange,
15};
16use rustc_index::IndexVec;
17use rustc_type_ir::{
18 FloatTy, IntTy, TypeVisitableExt as _, UintTy,
19 inherent::{GenericArgs as _, IntoKind},
20};
21use triomphe::Arc;
22
23use crate::{
24 ParamEnvAndCrate,
25 consteval::try_const_usize,
26 db::HirDatabase,
27 next_solver::{
28 Const, ConstKind, DbInterner, GenericArgs, PatternKind, StoredTy, Ty, TyKind, TypingMode,
29 ValueConst,
30 infer::{DbInternerInferExt, traits::ObligationCause},
31 },
32 traits::StoredParamEnvAndCrate,
33};
34
35pub use self::{adt::layout_of_adt_query, target::target_data_layout_query};
36
37pub(crate) mod adt;
38pub(crate) mod target;
39
40#[cfg(test)]
41mod tests;
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44pub struct RustcEnumVariantIdx(pub usize);
45
46impl rustc_index::Idx for RustcEnumVariantIdx {
47 fn new(idx: usize) -> Self {
48 RustcEnumVariantIdx(idx)
49 }
50
51 fn index(self) -> usize {
52 self.0
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
57pub struct RustcFieldIdx(pub LocalFieldId);
58
59impl RustcFieldIdx {
60 pub fn new(idx: usize) -> Self {
61 RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
62 }
63}
64
65impl rustc_index::Idx for RustcFieldIdx {
66 fn new(idx: usize) -> Self {
67 RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
68 }
69
70 fn index(self) -> usize {
71 u32::from(self.0.into_raw()) as usize
72 }
73}
74
75pub type Layout = LayoutData<RustcFieldIdx, RustcEnumVariantIdx>;
76pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>;
77pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx>;
78
79#[derive(Debug, PartialEq, Eq, Clone)]
80pub enum LayoutError {
81 BadCalc(LayoutCalculatorError<()>),
83 HasErrorConst,
84 HasErrorType,
85 HasPlaceholder,
86 InvalidSimdType,
87 NotImplemented,
88 RecursiveTypeWithoutIndirection,
89 TargetLayoutNotAvailable,
90 Unknown,
91 UserReprTooSmall,
92}
93
94impl std::error::Error for LayoutError {}
95impl fmt::Display for LayoutError {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 match self {
98 LayoutError::BadCalc(err) => err.fallback_fmt(f),
99 LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"),
100 LayoutError::HasErrorType => write!(f, "type contains an error"),
101 LayoutError::HasPlaceholder => write!(f, "type contains placeholders"),
102 LayoutError::InvalidSimdType => write!(f, "invalid simd type definition"),
103 LayoutError::NotImplemented => write!(f, "not implemented"),
104 LayoutError::RecursiveTypeWithoutIndirection => {
105 write!(f, "recursive type without indirection")
106 }
107 LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"),
108 LayoutError::Unknown => write!(f, "unknown"),
109 LayoutError::UserReprTooSmall => {
110 write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum")
111 }
112 }
113 }
114}
115
116impl<F> From<LayoutCalculatorError<F>> for LayoutError {
117 fn from(err: LayoutCalculatorError<F>) -> Self {
118 LayoutError::BadCalc(err.without_payload())
119 }
120}
121
122struct LayoutCx<'a> {
123 calc: LayoutCalculator<&'a TargetDataLayout>,
124}
125
126impl<'a> LayoutCx<'a> {
127 fn new(target: &'a TargetDataLayout) -> Self {
128 Self { calc: LayoutCalculator::new(target) }
129 }
130}
131
132fn layout_of_simd_ty<'db>(
134 db: &'db dyn HirDatabase,
135 id: StructId,
136 repr_packed: bool,
137 args: &GenericArgs<'db>,
138 env: ParamEnvAndCrate<'db>,
139 dl: &TargetDataLayout,
140) -> Result<Arc<Layout>, LayoutError> {
141 let fields = db.field_types(id.into());
147 let mut fields = fields.iter();
148 let Some(TyKind::Array(e_ty, e_len)) =
149 fields.next().filter(|_| fields.next().is_none()).map(|f| {
150 (*f.1).ty().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip().kind()
151 })
152 else {
153 return Err(LayoutError::InvalidSimdType);
154 };
155
156 let e_len = try_const_usize(db, e_len).ok_or(LayoutError::HasErrorConst)? as u64;
157 let e_ly = db.layout_of_ty(e_ty.store(), env.store())?;
158
159 let cx = LayoutCx::new(dl);
160 Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
161}
162
163#[salsa::tracked(cycle_result = layout_of_ty_cycle_result)]
164pub fn layout_of_ty_query(
165 db: &dyn HirDatabase,
166 ty: StoredTy,
167 trait_env: StoredParamEnvAndCrate,
168) -> Result<Arc<Layout>, LayoutError> {
169 let krate = trait_env.krate;
170 let interner = DbInterner::new_with(db, krate);
171 let Ok(target) = db.target_data_layout(krate) else {
172 return Err(LayoutError::TargetLayoutNotAvailable);
173 };
174 let dl = target;
175 let cx = LayoutCx::new(dl);
176 let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
177 let cause = ObligationCause::dummy();
178 let ty = infer_ctxt
179 .at(&cause, trait_env.param_env(db))
180 .deeply_normalize(ty.as_ref())
181 .unwrap_or(ty.as_ref());
182 let result = match ty.kind() {
183 TyKind::Adt(def, args) => {
184 match def.def_id() {
185 hir_def::AdtId::StructId(s) => {
186 let repr = AttrFlags::repr(db, s.into()).unwrap_or_default();
187 if repr.simd() {
188 return layout_of_simd_ty(
189 db,
190 s,
191 repr.packed(),
192 &args,
193 trait_env.as_ref(db),
194 target,
195 );
196 }
197 }
198 _ => {}
199 }
200 return db.layout_of_adt(def.def_id(), args.store(), trait_env);
201 }
202 TyKind::Bool => Layout::scalar(
203 dl,
204 Scalar::Initialized {
205 value: Primitive::Int(Integer::I8, false),
206 valid_range: WrappingRange { start: 0, end: 1 },
207 },
208 ),
209 TyKind::Char => Layout::scalar(
210 dl,
211 Scalar::Initialized {
212 value: Primitive::Int(Integer::I32, false),
213 valid_range: WrappingRange { start: 0, end: 0x10FFFF },
214 },
215 ),
216 TyKind::Int(i) => Layout::scalar(
217 dl,
218 scalar_unit(
219 dl,
220 Primitive::Int(
221 match i {
222 IntTy::Isize => dl.ptr_sized_integer(),
223 IntTy::I8 => Integer::I8,
224 IntTy::I16 => Integer::I16,
225 IntTy::I32 => Integer::I32,
226 IntTy::I64 => Integer::I64,
227 IntTy::I128 => Integer::I128,
228 },
229 true,
230 ),
231 ),
232 ),
233 TyKind::Uint(i) => Layout::scalar(
234 dl,
235 scalar_unit(
236 dl,
237 Primitive::Int(
238 match i {
239 UintTy::Usize => dl.ptr_sized_integer(),
240 UintTy::U8 => Integer::I8,
241 UintTy::U16 => Integer::I16,
242 UintTy::U32 => Integer::I32,
243 UintTy::U64 => Integer::I64,
244 UintTy::U128 => Integer::I128,
245 },
246 false,
247 ),
248 ),
249 ),
250 TyKind::Float(f) => Layout::scalar(
251 dl,
252 scalar_unit(
253 dl,
254 Primitive::Float(match f {
255 FloatTy::F16 => Float::F16,
256 FloatTy::F32 => Float::F32,
257 FloatTy::F64 => Float::F64,
258 FloatTy::F128 => Float::F128,
259 }),
260 ),
261 ),
262 TyKind::Tuple(tys) => {
263 let kind =
264 if tys.is_empty() { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
265
266 let fields = tys
267 .iter()
268 .map(|k| db.layout_of_ty(k.store(), trait_env.clone()))
269 .collect::<Result<Vec<_>, _>>()?;
270 let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
271 let fields = fields.iter().collect::<IndexVec<_, _>>();
272 cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
273 }
274 TyKind::Array(element, count) => {
275 let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
276 let element = db.layout_of_ty(element.store(), trait_env)?;
277 cx.calc.array_like::<_, _, ()>(&element, Some(count))?
278 }
279 TyKind::Slice(element) => {
280 let element = db.layout_of_ty(element.store(), trait_env)?;
281 cx.calc.array_like::<_, _, ()>(&element, None)?
282 }
283 TyKind::Str => {
284 let element = scalar_unit(dl, Primitive::Int(Integer::I8, false));
285 cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)?
286 }
287 TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => {
289 let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
290 if matches!(ty.kind(), TyKind::Ref(..)) {
291 data_ptr.valid_range_mut().start = 1;
292 }
293
294 let unsized_part = struct_tail_erasing_lifetimes(db, pointee);
301 let metadata = match unsized_part.kind() {
313 TyKind::Slice(_) | TyKind::Str => {
314 scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
315 }
316 TyKind::Dynamic(..) => {
317 let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
318 vtable.valid_range_mut().start = 1;
319 vtable
320 }
321 _ => {
322 return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
324 }
325 };
326
327 LayoutData::scalar_pair(dl, data_ptr, metadata)
329 }
330 TyKind::Never => LayoutData::never_type(dl),
331 TyKind::FnDef(..) => LayoutData::unit(dl, true),
332 TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false),
333 TyKind::FnPtr(..) => {
334 let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space));
335 ptr.valid_range_mut().start = 1;
336 Layout::scalar(dl, ptr)
337 }
338 TyKind::Closure(_, args) => {
339 return db.layout_of_ty(args.as_closure().tupled_upvars_ty().store(), trait_env);
340 }
341 TyKind::Coroutine(_, args) => {
342 return db.layout_of_ty(args.as_coroutine().tupled_upvars_ty().store(), trait_env);
343 }
344 TyKind::CoroutineClosure(_, args) => {
345 return db
346 .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env);
347 }
348 TyKind::CoroutineWitness(_, _) => {
349 return Err(LayoutError::NotImplemented);
350 }
351
352 TyKind::Pat(ty, pat) => {
353 let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone();
354 match pat.kind() {
355 PatternKind::Range { start, end } => {
356 if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
357 scalar.valid_range_mut().start = extract_const_value(start)?
358 .try_to_bits(db, trait_env.as_ref(db))
359 .ok_or(LayoutError::Unknown)?;
360
361 scalar.valid_range_mut().end = extract_const_value(end)?
362 .try_to_bits(db, trait_env.as_ref(db))
363 .ok_or(LayoutError::Unknown)?;
364
365 if scalar.is_signed() {
371 let range = scalar.valid_range_mut();
372 let start = layout.size.sign_extend(range.start);
373 let end = layout.size.sign_extend(range.end);
374 if end < start {
375 return Err(LayoutError::HasErrorType);
376 }
377 } else {
378 let range = scalar.valid_range_mut();
379 if range.end < range.start {
380 return Err(LayoutError::HasErrorType);
381 }
382 };
383
384 let niche = Niche {
385 offset: Size::ZERO,
386 value: scalar.primitive(),
387 valid_range: scalar.valid_range(target),
388 };
389
390 layout.largest_niche = Some(niche);
391 } else {
392 panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}")
393 }
394 }
395 PatternKind::NotNull => {
396 if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
397 &mut layout.backend_repr
398 {
399 scalar.valid_range_mut().start = 1;
400 let niche = Niche {
401 offset: Size::ZERO,
402 value: scalar.primitive(),
403 valid_range: scalar.valid_range(target),
404 };
405
406 layout.largest_niche = Some(niche);
407 } else {
408 panic!(
409 "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}"
410 )
411 }
412 }
413
414 PatternKind::Or(variants) => match variants[0].kind() {
415 PatternKind::Range { .. } => {
416 if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
417 let variants: Result<Vec<_>, _> = variants
418 .iter()
419 .map(|pat| match pat.kind() {
420 PatternKind::Range { start, end } => Ok::<_, LayoutError>((
421 extract_const_value(start)?
422 .try_to_bits(db, trait_env.as_ref(db))
423 .ok_or(LayoutError::Unknown)?,
424 extract_const_value(end)?
425 .try_to_bits(db, trait_env.as_ref(db))
426 .ok_or(LayoutError::Unknown)?,
427 )),
428 PatternKind::NotNull | PatternKind::Or(_) => {
429 Err(LayoutError::Unknown)
430 }
431 })
432 .collect();
433 let mut variants = variants?;
434 if !scalar.is_signed() {
435 return Err(LayoutError::HasErrorType);
436 }
437 variants.sort();
438 if variants.len() != 2 {
439 return Err(LayoutError::HasErrorType);
440 }
441
442 let mut first = variants[0];
444 let mut second = variants[1];
445 if second.0
446 == layout.size.truncate(layout.size.signed_int_min() as u128)
447 {
448 (second, first) = (first, second);
449 }
450
451 if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0)
452 {
453 return Err(LayoutError::HasErrorType);
454 }
455 if layout.size.signed_int_max() as u128 != second.1 {
456 return Err(LayoutError::HasErrorType);
457 }
458
459 scalar.valid_range_mut().start = second.0;
461 scalar.valid_range_mut().end = first.1;
462
463 let niche = Niche {
464 offset: Size::ZERO,
465 value: scalar.primitive(),
466 valid_range: scalar.valid_range(target),
467 };
468
469 layout.largest_niche = Some(niche);
470 } else {
471 panic!(
472 "pattern type with range but not scalar layout: {ty:?}, {layout:?}"
473 )
474 }
475 }
476 PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"),
477 PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"),
478 },
479 }
480 layout.fields = FieldsShape::Arbitrary {
485 offsets: [Size::ZERO].into_iter().collect(),
486 in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(),
487 };
488 layout
489 }
490 TyKind::UnsafeBinder(_) => {
491 return Err(LayoutError::NotImplemented);
492 }
493
494 TyKind::Error(_) => return Err(LayoutError::HasErrorType),
495 TyKind::Placeholder(_)
496 | TyKind::Bound(..)
497 | TyKind::Infer(..)
498 | TyKind::Param(..)
499 | TyKind::Alias(..) => {
500 return Err(LayoutError::HasPlaceholder);
501 }
502 };
503 Ok(Arc::new(result))
504}
505
506fn layout_of_ty_cycle_result(
507 _: &dyn HirDatabase,
508 _: salsa::Id,
509 _: StoredTy,
510 _: StoredParamEnvAndCrate,
511) -> Result<Arc<Layout>, LayoutError> {
512 Err(LayoutError::RecursiveTypeWithoutIndirection)
513}
514
515fn extract_const_value<'db>(ct: Const<'db>) -> Result<ValueConst<'db>, LayoutError> {
516 match ct.kind() {
517 ConstKind::Value(cv) => Ok(cv),
518 ConstKind::Param(_)
519 | ConstKind::Expr(_)
520 | ConstKind::Unevaluated(_)
521 | ConstKind::Infer(_)
522 | ConstKind::Bound(..)
523 | ConstKind::Placeholder(_) => {
524 if ct.has_param() {
525 Err(LayoutError::HasPlaceholder)
526 } else {
527 Err(LayoutError::Unknown)
528 }
529 }
530 ConstKind::Error(_) => Err(LayoutError::HasErrorConst),
531 }
532}
533
534fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
535 match pointee.kind() {
536 TyKind::Adt(def, args) => {
537 let struct_id = match def.def_id() {
538 AdtId::StructId(id) => id,
539 _ => return pointee,
540 };
541 let data = struct_id.fields(db);
542 let mut it = data.fields().iter().rev();
543 match it.next() {
544 Some((f, _)) => {
545 let last_field_ty = field_ty(db, struct_id.into(), f, args);
546 struct_tail_erasing_lifetimes(db, last_field_ty)
547 }
548 None => pointee,
549 }
550 }
551 TyKind::Tuple(tys) => {
552 if let Some(last_field_ty) = tys.iter().next_back() {
553 struct_tail_erasing_lifetimes(db, last_field_ty)
554 } else {
555 pointee
556 }
557 }
558 _ => pointee,
559 }
560}
561
562fn field_ty<'a>(
563 db: &'a dyn HirDatabase,
564 def: hir_def::VariantId,
565 fd: LocalFieldId,
566 args: GenericArgs<'a>,
567) -> Ty<'a> {
568 db.field_types(def)[fd].ty().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip()
569}
570
571fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
572 Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
573}