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 representability::{Representability, representability},
33 traits::StoredParamEnvAndCrate,
34};
35
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: ParamEnvAndCrate<'db>,
137 dl: &TargetDataLayout,
138) -> Result<Arc<Layout>, LayoutError> {
139 let fields = db.field_types(id.into());
145 let mut fields = fields.iter();
146 let Some(TyKind::Array(e_ty, e_len)) =
147 fields.next().filter(|_| fields.next().is_none()).map(|f| {
148 (*f.1).ty().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip().kind()
149 })
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.store(), env.store())?;
156
157 let cx = LayoutCx::new(dl);
158 Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
159}
160
161#[salsa::tracked(cycle_result = layout_of_ty_cycle_result, returns(clone))]
162pub fn layout_of_ty_query(
163 db: &dyn HirDatabase,
164 ty: StoredTy,
165 trait_env: StoredParamEnvAndCrate,
166) -> Result<Arc<Layout>, LayoutError> {
167 let krate = trait_env.krate;
168 let interner = DbInterner::new_with(db, krate);
169 let Ok(target) = db.target_data_layout(krate) else {
170 return Err(LayoutError::TargetLayoutNotAvailable);
171 };
172 let dl = target;
173 let cx = LayoutCx::new(dl);
174 let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
175 let cause = ObligationCause::dummy();
176 let ty = infer_ctxt
177 .at(&cause, trait_env.param_env(db))
178 .deeply_normalize(ty.as_ref())
179 .unwrap_or(ty.as_ref());
180 let result = match ty.kind() {
181 TyKind::Adt(def, args) => {
182 match def.def_id() {
183 hir_def::AdtId::StructId(s) => {
184 let repr = AttrFlags::repr(db, s.into()).unwrap_or_default();
185 if repr.simd() {
186 return layout_of_simd_ty(
187 db,
188 s,
189 repr.packed(),
190 &args,
191 trait_env.as_ref(db),
192 target,
193 );
194 }
195 }
196 _ => {}
197 }
198 return db.layout_of_adt(def.def_id(), args.store(), trait_env);
199 }
200 TyKind::Bool => Layout::scalar(
201 dl,
202 Scalar::Initialized {
203 value: Primitive::Int(Integer::I8, false),
204 valid_range: WrappingRange { start: 0, end: 1 },
205 },
206 ),
207 TyKind::Char => Layout::scalar(
208 dl,
209 Scalar::Initialized {
210 value: Primitive::Int(Integer::I32, false),
211 valid_range: WrappingRange { start: 0, end: 0x10FFFF },
212 },
213 ),
214 TyKind::Int(i) => Layout::scalar(
215 dl,
216 scalar_unit(
217 dl,
218 Primitive::Int(
219 match i {
220 IntTy::Isize => dl.ptr_sized_integer(),
221 IntTy::I8 => Integer::I8,
222 IntTy::I16 => Integer::I16,
223 IntTy::I32 => Integer::I32,
224 IntTy::I64 => Integer::I64,
225 IntTy::I128 => Integer::I128,
226 },
227 true,
228 ),
229 ),
230 ),
231 TyKind::Uint(i) => Layout::scalar(
232 dl,
233 scalar_unit(
234 dl,
235 Primitive::Int(
236 match i {
237 UintTy::Usize => dl.ptr_sized_integer(),
238 UintTy::U8 => Integer::I8,
239 UintTy::U16 => Integer::I16,
240 UintTy::U32 => Integer::I32,
241 UintTy::U64 => Integer::I64,
242 UintTy::U128 => Integer::I128,
243 },
244 false,
245 ),
246 ),
247 ),
248 TyKind::Float(f) => Layout::scalar(
249 dl,
250 scalar_unit(
251 dl,
252 Primitive::Float(match f {
253 FloatTy::F16 => Float::F16,
254 FloatTy::F32 => Float::F32,
255 FloatTy::F64 => Float::F64,
256 FloatTy::F128 => Float::F128,
257 }),
258 ),
259 ),
260 TyKind::Tuple(tys) => {
261 let kind =
262 if tys.is_empty() { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
263
264 let fields = tys
265 .iter()
266 .map(|k| db.layout_of_ty(k.store(), trait_env.clone()))
267 .collect::<Result<Vec<_>, _>>()?;
268 let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
269 let fields = fields.iter().collect::<IndexVec<_, _>>();
270 cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
271 }
272 TyKind::Array(element, count) => {
273 let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
274 let element = db.layout_of_ty(element.store(), trait_env)?;
275 cx.calc.array_like::<_, _, ()>(&element, Some(count))?
276 }
277 TyKind::Slice(element) => {
278 let element = db.layout_of_ty(element.store(), trait_env)?;
279 cx.calc.array_like::<_, _, ()>(&element, None)?
280 }
281 TyKind::Str => {
282 let element = scalar_unit(dl, Primitive::Int(Integer::I8, false));
283 cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)?
284 }
285 TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => {
287 let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
288 if matches!(ty.kind(), TyKind::Ref(..)) {
289 data_ptr.valid_range_mut().start = 1;
290 }
291
292 let unsized_part = struct_tail_erasing_lifetimes(db, pointee);
299 let metadata = match unsized_part.kind() {
311 TyKind::Slice(_) | TyKind::Str => {
312 scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
313 }
314 TyKind::Dynamic(..) => {
315 let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
316 vtable.valid_range_mut().start = 1;
317 vtable
318 }
319 _ => {
320 return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
322 }
323 };
324
325 LayoutData::scalar_pair(dl, data_ptr, metadata)
327 }
328 TyKind::Never => LayoutData::never_type(dl),
329 TyKind::FnDef(..) => LayoutData::unit(dl, true),
330 TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false),
331 TyKind::FnPtr(..) => {
332 let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space));
333 ptr.valid_range_mut().start = 1;
334 Layout::scalar(dl, ptr)
335 }
336 TyKind::Closure(_, args) => {
337 return db.layout_of_ty(args.as_closure().tupled_upvars_ty().store(), trait_env);
338 }
339 TyKind::Coroutine(_, args) => {
340 return db.layout_of_ty(args.as_coroutine().tupled_upvars_ty().store(), trait_env);
341 }
342 TyKind::CoroutineClosure(_, args) => {
343 return db
344 .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env);
345 }
346 TyKind::CoroutineWitness(_, _) => {
347 return Err(LayoutError::NotImplemented);
348 }
349
350 TyKind::Pat(ty, pat) => {
351 let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone();
352 match pat.kind() {
353 PatternKind::Range { start, end } => {
354 if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
355 scalar.valid_range_mut().start = extract_const_value(start)?
356 .try_to_bits(db, trait_env.as_ref(db))
357 .ok_or(LayoutError::Unknown)?;
358
359 scalar.valid_range_mut().end = extract_const_value(end)?
360 .try_to_bits(db, trait_env.as_ref(db))
361 .ok_or(LayoutError::Unknown)?;
362
363 if scalar.is_signed() {
369 let range = scalar.valid_range_mut();
370 let start = layout.size.sign_extend(range.start);
371 let end = layout.size.sign_extend(range.end);
372 if end < start {
373 return Err(LayoutError::HasErrorType);
374 }
375 } else {
376 let range = scalar.valid_range_mut();
377 if range.end < range.start {
378 return Err(LayoutError::HasErrorType);
379 }
380 };
381
382 let niche = Niche {
383 offset: Size::ZERO,
384 value: scalar.primitive(),
385 valid_range: scalar.valid_range(target),
386 };
387
388 layout.largest_niche = Some(niche);
389 } else {
390 panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}")
391 }
392 }
393 PatternKind::NotNull => {
394 if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
395 &mut layout.backend_repr
396 {
397 scalar.valid_range_mut().start = 1;
398 let niche = Niche {
399 offset: Size::ZERO,
400 value: scalar.primitive(),
401 valid_range: scalar.valid_range(target),
402 };
403
404 layout.largest_niche = Some(niche);
405 } else {
406 panic!(
407 "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}"
408 )
409 }
410 }
411
412 PatternKind::Or(variants) => match variants[0].kind() {
413 PatternKind::Range { .. } => {
414 if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
415 let variants: Result<Vec<_>, _> = variants
416 .iter()
417 .map(|pat| match pat.kind() {
418 PatternKind::Range { start, end } => Ok::<_, LayoutError>((
419 extract_const_value(start)?
420 .try_to_bits(db, trait_env.as_ref(db))
421 .ok_or(LayoutError::Unknown)?,
422 extract_const_value(end)?
423 .try_to_bits(db, trait_env.as_ref(db))
424 .ok_or(LayoutError::Unknown)?,
425 )),
426 PatternKind::NotNull | PatternKind::Or(_) => {
427 Err(LayoutError::Unknown)
428 }
429 })
430 .collect();
431 let mut variants = variants?;
432 if !scalar.is_signed() {
433 return Err(LayoutError::HasErrorType);
434 }
435 variants.sort();
436 if variants.len() != 2 {
437 return Err(LayoutError::HasErrorType);
438 }
439
440 let mut first = variants[0];
442 let mut second = variants[1];
443 if second.0
444 == layout.size.truncate(layout.size.signed_int_min() as u128)
445 {
446 (second, first) = (first, second);
447 }
448
449 if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0)
450 {
451 return Err(LayoutError::HasErrorType);
452 }
453 if layout.size.signed_int_max() as u128 != second.1 {
454 return Err(LayoutError::HasErrorType);
455 }
456
457 scalar.valid_range_mut().start = second.0;
459 scalar.valid_range_mut().end = first.1;
460
461 let niche = Niche {
462 offset: Size::ZERO,
463 value: scalar.primitive(),
464 valid_range: scalar.valid_range(target),
465 };
466
467 layout.largest_niche = Some(niche);
468 } else {
469 panic!(
470 "pattern type with range but not scalar layout: {ty:?}, {layout:?}"
471 )
472 }
473 }
474 PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"),
475 PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"),
476 },
477 }
478 layout.fields = FieldsShape::Arbitrary {
483 offsets: [Size::ZERO].into_iter().collect(),
484 in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(),
485 };
486 layout
487 }
488 TyKind::UnsafeBinder(_) => {
489 return Err(LayoutError::NotImplemented);
490 }
491
492 TyKind::Error(_) => return Err(LayoutError::HasErrorType),
493 TyKind::Placeholder(_)
494 | TyKind::Bound(..)
495 | TyKind::Infer(..)
496 | TyKind::Param(..)
497 | TyKind::Alias(..) => {
498 return Err(LayoutError::HasPlaceholder);
499 }
500 };
501 Ok(Arc::new(result))
502}
503
504fn layout_of_ty_cycle_result(
505 _: &dyn HirDatabase,
506 _: salsa::Id,
507 _: StoredTy,
508 _: StoredParamEnvAndCrate,
509) -> Result<Arc<Layout>, LayoutError> {
510 Err(LayoutError::RecursiveTypeWithoutIndirection)
511}
512
513fn extract_const_value<'db>(ct: Const<'db>) -> Result<ValueConst<'db>, LayoutError> {
514 match ct.kind() {
515 ConstKind::Value(cv) => Ok(cv),
516 ConstKind::Param(_)
517 | ConstKind::Expr(_)
518 | ConstKind::Unevaluated(_)
519 | ConstKind::Infer(_)
520 | ConstKind::Bound(..)
521 | ConstKind::Placeholder(_) => {
522 if ct.has_param() {
523 Err(LayoutError::HasPlaceholder)
524 } else {
525 Err(LayoutError::Unknown)
526 }
527 }
528 ConstKind::Error(_) => Err(LayoutError::HasErrorConst),
529 }
530}
531
532fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
533 match pointee.kind() {
534 TyKind::Adt(def, args) => {
535 let struct_id = match def.def_id() {
536 AdtId::StructId(id) => id,
537 _ => return pointee,
538 };
539 if representability(db, def.def_id()) == Representability::Infinite {
540 return pointee;
541 }
542 let data = struct_id.fields(db);
543 let mut it = data.fields().iter().rev();
544 match it.next() {
545 Some((f, _)) => {
546 let last_field_ty = field_ty(db, struct_id.into(), f, args);
547 struct_tail_erasing_lifetimes(db, last_field_ty)
548 }
549 None => pointee,
550 }
551 }
552 TyKind::Tuple(tys) => {
553 if let Some(last_field_ty) = tys.iter().next_back() {
554 struct_tail_erasing_lifetimes(db, last_field_ty)
555 } else {
556 pointee
557 }
558 }
559 _ => pointee,
560 }
561}
562
563fn field_ty<'a>(
564 db: &'a dyn HirDatabase,
565 def: hir_def::VariantId,
566 fd: LocalFieldId,
567 args: GenericArgs<'a>,
568) -> Ty<'a> {
569 db.field_types(def)[fd].ty().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip()
570}
571
572fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
573 Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
574}