1use hir::def_id::DefId;
2use rustc_abi::Integer::{I8, I32};
3use rustc_abi::Primitive::{self, Float, Int, Pointer};
4use rustc_abi::{
5 AddressSpace, BackendRepr, FIRST_VARIANT, FieldIdx, FieldsShape, HasDataLayout, Layout,
6 LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding,
7 VariantIdx, Variants, WrappingRange,
8};
9use rustc_hashes::Hash64;
10use rustc_index::IndexVec;
11use rustc_middle::bug;
12use rustc_middle::query::Providers;
13use rustc_middle::ty::layout::{
14 FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
15};
16use rustc_middle::ty::print::with_no_trimmed_paths;
17use rustc_middle::ty::{
18 self, AdtDef, CoroutineArgsExt, EarlyBinder, PseudoCanonicalInput, Ty, TyCtxt, TypeVisitableExt,
19};
20use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
21use rustc_span::{Symbol, sym};
22use tracing::{debug, instrument};
23use {rustc_abi as abi, rustc_hir as hir};
24
25use crate::errors::{NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType};
26
27mod invariant;
28
29pub(crate) fn provide(providers: &mut Providers) {
30 *providers = Providers { layout_of, ..*providers };
31}
32
33#[instrument(skip(tcx, query), level = "debug")]
34fn layout_of<'tcx>(
35 tcx: TyCtxt<'tcx>,
36 query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
37) -> Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>> {
38 let PseudoCanonicalInput { typing_env, value: ty } = query;
39 debug!(?ty);
40
41 let typing_env = typing_env.with_post_analysis_normalized(tcx);
45 let unnormalized_ty = ty;
46
47 let ty = match tcx.try_normalize_erasing_regions(typing_env, ty) {
52 Ok(t) => t,
53 Err(normalization_error) => {
54 return Err(tcx
55 .arena
56 .alloc(LayoutError::NormalizationFailure(ty, normalization_error)));
57 }
58 };
59
60 if ty != unnormalized_ty {
61 return tcx.layout_of(typing_env.as_query_input(ty));
63 }
64
65 let cx = LayoutCx::new(tcx, typing_env);
66
67 let layout = layout_of_uncached(&cx, ty)?;
68 let layout = TyAndLayout { ty, layout };
69
70 if cx.tcx().sess.opts.unstable_opts.print_type_sizes {
73 record_layout_for_printing(&cx, layout);
74 }
75
76 invariant::layout_sanity_check(&cx, &layout);
77
78 Ok(layout)
79}
80
81fn error<'tcx>(cx: &LayoutCx<'tcx>, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> {
82 cx.tcx().arena.alloc(err)
83}
84
85fn map_error<'tcx>(
86 cx: &LayoutCx<'tcx>,
87 ty: Ty<'tcx>,
88 err: LayoutCalculatorError<TyAndLayout<'tcx>>,
89) -> &'tcx LayoutError<'tcx> {
90 let err = match err {
91 LayoutCalculatorError::SizeOverflow => {
92 LayoutError::SizeOverflow(ty)
95 }
96 LayoutCalculatorError::UnexpectedUnsized(field) => {
97 assert!(field.layout.is_unsized(), "invalid layout error {err:#?}");
100 if cx.typing_env.param_env.caller_bounds().is_empty() {
101 cx.tcx().dcx().delayed_bug(format!(
102 "encountered unexpected unsized field in layout of {ty:?}: {field:#?}"
103 ));
104 }
105 LayoutError::Unknown(ty)
106 }
107 LayoutCalculatorError::EmptyUnion => {
108 let guar =
110 cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}"));
111 LayoutError::ReferencesError(guar)
112 }
113 LayoutCalculatorError::ReprConflict => {
114 let guar = cx
116 .tcx()
117 .dcx()
118 .delayed_bug(format!("computed impossible repr (packed enum?): {ty:?}"));
119 LayoutError::ReferencesError(guar)
120 }
121 LayoutCalculatorError::ZeroLengthSimdType => {
122 cx.tcx().dcx().emit_fatal(ZeroLengthSimdType { ty })
124 }
125 LayoutCalculatorError::OversizedSimdType { max_lanes } => {
126 cx.tcx().dcx().emit_fatal(OversizedSimdType { ty, max_lanes })
128 }
129 LayoutCalculatorError::NonPrimitiveSimdType(field) => {
130 cx.tcx().dcx().emit_fatal(NonPrimitiveSimdType { ty, e_ty: field.ty })
133 }
134 };
135 error(cx, err)
136}
137
138fn extract_const_value<'tcx>(
139 cx: &LayoutCx<'tcx>,
140 ty: Ty<'tcx>,
141 ct: ty::Const<'tcx>,
142) -> Result<ty::Value<'tcx>, &'tcx LayoutError<'tcx>> {
143 match ct.kind() {
144 ty::ConstKind::Value(cv) => Ok(cv),
145 ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) => {
146 if !ct.has_param() {
147 bug!("failed to normalize const, but it is not generic: {ct:?}");
148 }
149 Err(error(cx, LayoutError::TooGeneric(ty)))
150 }
151 ty::ConstKind::Unevaluated(_) => {
152 let err = if ct.has_param() {
153 LayoutError::TooGeneric(ty)
154 } else {
155 LayoutError::Unknown(ty)
161 };
162 Err(error(cx, err))
163 }
164 ty::ConstKind::Infer(_)
165 | ty::ConstKind::Bound(..)
166 | ty::ConstKind::Placeholder(_)
167 | ty::ConstKind::Error(_) => {
168 bug!("layout_of: unexpected const: {ct:?}");
171 }
172 }
173}
174
175fn layout_of_uncached<'tcx>(
176 cx: &LayoutCx<'tcx>,
177 ty: Ty<'tcx>,
178) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
179 if let Err(guar) = ty.error_reported() {
183 return Err(error(cx, LayoutError::ReferencesError(guar)));
184 }
185
186 let tcx = cx.tcx();
187 let dl = cx.data_layout();
188 let map_layout = |result: Result<_, _>| match result {
189 Ok(layout) => Ok(tcx.mk_layout(layout)),
190 Err(err) => Err(map_error(cx, ty, err)),
191 };
192 let scalar_unit = |value: Primitive| {
193 let size = value.size(dl);
194 assert!(size.bits() <= 128);
195 Scalar::Initialized { value, valid_range: WrappingRange::full(size) }
196 };
197 let scalar = |value: Primitive| tcx.mk_layout(LayoutData::scalar(cx, scalar_unit(value)));
198
199 let univariant = |tys: &[Ty<'tcx>], kind| {
200 let fields = tys.iter().map(|ty| cx.layout_of(*ty)).try_collect::<IndexVec<_, _>>()?;
201 let repr = ReprOptions::default();
202 map_layout(cx.calc.univariant(&fields, &repr, kind))
203 };
204 debug_assert!(!ty.has_non_region_infer());
205
206 Ok(match *ty.kind() {
207 ty::Pat(ty, pat) => {
208 let layout = cx.layout_of(ty)?.layout;
209 let mut layout = LayoutData::clone(&layout.0);
210 match *pat {
211 ty::PatternKind::Range { start, end } => {
212 if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
213 &mut layout.backend_repr
214 {
215 scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
216 .try_to_bits(tcx, cx.typing_env)
217 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
218
219 scalar.valid_range_mut().end = extract_const_value(cx, ty, end)?
220 .try_to_bits(tcx, cx.typing_env)
221 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
222
223 if scalar.is_signed() {
229 let range = scalar.valid_range_mut();
230 let start = layout.size.sign_extend(range.start);
231 let end = layout.size.sign_extend(range.end);
232 if end < start {
233 let guar = tcx.dcx().err(format!(
234 "pattern type ranges cannot wrap: {start}..={end}"
235 ));
236
237 return Err(error(cx, LayoutError::ReferencesError(guar)));
238 }
239 } else {
240 let range = scalar.valid_range_mut();
241 if range.end < range.start {
242 let guar = tcx.dcx().err(format!(
243 "pattern type ranges cannot wrap: {}..={}",
244 range.start, range.end
245 ));
246
247 return Err(error(cx, LayoutError::ReferencesError(guar)));
248 }
249 };
250
251 let niche = Niche {
252 offset: Size::ZERO,
253 value: scalar.primitive(),
254 valid_range: scalar.valid_range(cx),
255 };
256
257 layout.largest_niche = Some(niche);
258
259 tcx.mk_layout(layout)
260 } else {
261 bug!("pattern type with range but not scalar layout: {ty:?}, {layout:?}")
262 }
263 }
264 }
265 }
266
267 ty::Bool => tcx.mk_layout(LayoutData::scalar(
269 cx,
270 Scalar::Initialized {
271 value: Int(I8, false),
272 valid_range: WrappingRange { start: 0, end: 1 },
273 },
274 )),
275 ty::Char => tcx.mk_layout(LayoutData::scalar(
276 cx,
277 Scalar::Initialized {
278 value: Int(I32, false),
279 valid_range: WrappingRange { start: 0, end: 0x10FFFF },
280 },
281 )),
282 ty::Int(ity) => scalar(Int(abi::Integer::from_int_ty(dl, ity), true)),
283 ty::Uint(ity) => scalar(Int(abi::Integer::from_uint_ty(dl, ity), false)),
284 ty::Float(fty) => scalar(Float(abi::Float::from_float_ty(fty))),
285 ty::FnPtr(..) => {
286 let mut ptr = scalar_unit(Pointer(dl.instruction_address_space));
287 ptr.valid_range_mut().start = 1;
288 tcx.mk_layout(LayoutData::scalar(cx, ptr))
289 }
290
291 ty::Never => tcx.mk_layout(LayoutData::never_type(cx)),
293
294 ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => {
296 let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA));
297 if !ty.is_raw_ptr() {
298 data_ptr.valid_range_mut().start = 1;
299 }
300
301 if pointee.is_sized(tcx, cx.typing_env) {
302 return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr)));
303 }
304
305 let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() {
306 let pointee_metadata = Ty::new_projection(tcx, metadata_def_id, [pointee]);
307 let metadata_ty =
308 match tcx.try_normalize_erasing_regions(cx.typing_env, pointee_metadata) {
309 Ok(metadata_ty) => metadata_ty,
310 Err(mut err) => {
311 match tcx.try_normalize_erasing_regions(
320 cx.typing_env,
321 tcx.struct_tail_raw(pointee, |ty| ty, || {}),
322 ) {
323 Ok(_) => {}
324 Err(better_err) => {
325 err = better_err;
326 }
327 }
328 return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
329 }
330 };
331
332 let metadata_layout = cx.layout_of(metadata_ty)?;
333 if metadata_layout.is_1zst() {
335 return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr)));
336 }
337
338 let BackendRepr::Scalar(metadata) = metadata_layout.backend_repr else {
339 return Err(error(cx, LayoutError::Unknown(pointee)));
340 };
341
342 metadata
343 } else {
344 let unsized_part = tcx.struct_tail_for_codegen(pointee, cx.typing_env);
345
346 match unsized_part.kind() {
347 ty::Foreign(..) => {
348 return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr)));
349 }
350 ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
351 ty::Dynamic(..) => {
352 let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
353 vtable.valid_range_mut().start = 1;
354 vtable
355 }
356 _ => {
357 return Err(error(cx, LayoutError::Unknown(pointee)));
358 }
359 }
360 };
361
362 tcx.mk_layout(LayoutData::scalar_pair(cx, data_ptr, metadata))
364 }
365
366 ty::Dynamic(_, _, ty::DynStar) => {
367 let mut data = scalar_unit(Pointer(AddressSpace::DATA));
368 data.valid_range_mut().start = 0;
369 let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
370 vtable.valid_range_mut().start = 1;
371 tcx.mk_layout(LayoutData::scalar_pair(cx, data, vtable))
372 }
373
374 ty::Array(element, count) => {
376 let count = extract_const_value(cx, ty, count)?
377 .try_to_target_usize(tcx)
378 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
379
380 let element = cx.layout_of(element)?;
381 map_layout(cx.calc.array_like(&element, Some(count)))?
382 }
383 ty::Slice(element) => {
384 let element = cx.layout_of(element)?;
385 map_layout(cx.calc.array_like(&element, None).map(|mut layout| {
386 layout.randomization_seed = Hash64::new(0x2dcba99c39784102);
388 layout
389 }))?
390 }
391 ty::Str => {
392 let element = scalar(Int(I8, false));
393 map_layout(cx.calc.array_like(&element, None).map(|mut layout| {
394 layout.randomization_seed = Hash64::new(0xc1325f37d127be22);
396 layout
397 }))?
398 }
399
400 ty::FnDef(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => {
402 let sized = matches!(ty.kind(), ty::FnDef(..));
403 tcx.mk_layout(LayoutData::unit(cx, sized))
404 }
405
406 ty::Coroutine(def_id, args) => {
407 use rustc_middle::ty::layout::PrimitiveExt as _;
408
409 let Some(info) = tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) else {
410 return Err(error(cx, LayoutError::Unknown(ty)));
411 };
412
413 let local_layouts = info
414 .field_tys
415 .iter()
416 .map(|local| {
417 let field_ty = EarlyBinder::bind(local.ty);
418 let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty.instantiate(tcx, args));
419 cx.spanned_layout_of(uninit_ty, local.source_info.span)
420 })
421 .try_collect::<IndexVec<_, _>>()?;
422
423 let prefix_layouts = args
424 .as_coroutine()
425 .prefix_tys()
426 .iter()
427 .map(|ty| cx.layout_of(ty))
428 .try_collect::<IndexVec<_, _>>()?;
429
430 let layout = cx
431 .calc
432 .coroutine(
433 &local_layouts,
434 prefix_layouts,
435 &info.variant_fields,
436 &info.storage_conflicts,
437 |tag| TyAndLayout {
438 ty: tag.primitive().to_ty(tcx),
439 layout: tcx.mk_layout(LayoutData::scalar(cx, tag)),
440 },
441 )
442 .map(|mut layout| {
443 layout.randomization_seed = tcx.def_path_hash(def_id).0.to_smaller_hash();
445 debug!("coroutine layout ({:?}): {:#?}", ty, layout);
446 layout
447 });
448 map_layout(layout)?
449 }
450
451 ty::Closure(_, args) => univariant(args.as_closure().upvar_tys(), StructKind::AlwaysSized)?,
452
453 ty::CoroutineClosure(_, args) => {
454 univariant(args.as_coroutine_closure().upvar_tys(), StructKind::AlwaysSized)?
455 }
456
457 ty::Tuple(tys) => {
458 let kind =
459 if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
460
461 univariant(tys, kind)?
462 }
463
464 ty::Adt(def, args) if def.repr().simd() => {
466 let Some(ty::Array(e_ty, e_len)) = def
472 .is_struct()
473 .then(|| &def.variant(FIRST_VARIANT).fields)
474 .filter(|fields| fields.len() == 1)
475 .map(|fields| *fields[FieldIdx::ZERO].ty(tcx, args).kind())
476 else {
477 let guar = tcx.dcx().delayed_bug("#[repr(simd)] was applied to an invalid ADT");
479 return Err(error(cx, LayoutError::ReferencesError(guar)));
480 };
481
482 let e_len = extract_const_value(cx, ty, e_len)?
483 .try_to_target_usize(tcx)
484 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
485
486 let e_ly = cx.layout_of(e_ty)?;
487
488 map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))?
489 }
490
491 ty::Adt(def, args) => {
493 let variants = def
495 .variants()
496 .iter()
497 .map(|v| {
498 v.fields
499 .iter()
500 .map(|field| cx.layout_of(field.ty(tcx, args)))
501 .try_collect::<IndexVec<_, _>>()
502 })
503 .try_collect::<IndexVec<VariantIdx, _>>()?;
504
505 if def.is_union() {
506 if def.repr().pack.is_some() && def.repr().align.is_some() {
507 let guar = tcx.dcx().span_delayed_bug(
508 tcx.def_span(def.did()),
509 "union cannot be packed and aligned",
510 );
511 return Err(error(cx, LayoutError::ReferencesError(guar)));
512 }
513
514 return map_layout(cx.calc.layout_of_union(&def.repr(), &variants));
515 }
516
517 let get_discriminant_type =
518 |min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max);
519
520 let discriminants_iter = || {
521 def.is_enum()
522 .then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val as i128)))
523 .into_iter()
524 .flatten()
525 };
526
527 let dont_niche_optimize_enum = def.repr().inhibit_enum_layout_opt()
528 || def
529 .variants()
530 .iter_enumerated()
531 .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()));
532
533 let maybe_unsized = def.is_struct()
534 && def.non_enum_variant().tail_opt().is_some_and(|last_field| {
535 let typing_env = ty::TypingEnv::post_analysis(tcx, def.did());
536 !tcx.type_of(last_field.did).instantiate_identity().is_sized(tcx, typing_env)
537 });
538
539 let layout = cx
540 .calc
541 .layout_of_struct_or_enum(
542 &def.repr(),
543 &variants,
544 def.is_enum(),
545 def.is_unsafe_cell(),
546 tcx.layout_scalar_valid_range(def.did()),
547 get_discriminant_type,
548 discriminants_iter(),
549 dont_niche_optimize_enum,
550 !maybe_unsized,
551 )
552 .map_err(|err| map_error(cx, ty, err))?;
553
554 if !maybe_unsized && layout.is_unsized() {
555 bug!("got unsized layout for type that cannot be unsized {ty:?}: {layout:#?}");
556 }
557
558 if cfg!(debug_assertions)
560 && maybe_unsized
561 && def.non_enum_variant().tail().ty(tcx, args).is_sized(tcx, cx.typing_env)
562 {
563 let mut variants = variants;
564 let tail_replacement = cx.layout_of(Ty::new_slice(tcx, tcx.types.u8)).unwrap();
565 *variants[FIRST_VARIANT].raw.last_mut().unwrap() = tail_replacement;
566
567 let Ok(unsized_layout) = cx.calc.layout_of_struct_or_enum(
568 &def.repr(),
569 &variants,
570 def.is_enum(),
571 def.is_unsafe_cell(),
572 tcx.layout_scalar_valid_range(def.did()),
573 get_discriminant_type,
574 discriminants_iter(),
575 dont_niche_optimize_enum,
576 !maybe_unsized,
577 ) else {
578 bug!("failed to compute unsized layout of {ty:?}");
579 };
580
581 let FieldsShape::Arbitrary { offsets: sized_offsets, .. } = &layout.fields else {
582 bug!("unexpected FieldsShape for sized layout of {ty:?}: {:?}", layout.fields);
583 };
584 let FieldsShape::Arbitrary { offsets: unsized_offsets, .. } =
585 &unsized_layout.fields
586 else {
587 bug!(
588 "unexpected FieldsShape for unsized layout of {ty:?}: {:?}",
589 unsized_layout.fields
590 );
591 };
592
593 let (sized_tail, sized_fields) = sized_offsets.raw.split_last().unwrap();
594 let (unsized_tail, unsized_fields) = unsized_offsets.raw.split_last().unwrap();
595
596 if sized_fields != unsized_fields {
597 bug!("unsizing {ty:?} changed field order!\n{layout:?}\n{unsized_layout:?}");
598 }
599
600 if sized_tail < unsized_tail {
601 bug!("unsizing {ty:?} moved tail backwards!\n{layout:?}\n{unsized_layout:?}");
602 }
603 }
604
605 tcx.mk_layout(layout)
606 }
607
608 ty::UnsafeBinder(bound_ty) => {
609 let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into());
610 cx.layout_of(ty)?.layout
611 }
612
613 ty::Param(_) | ty::Placeholder(..) => {
615 return Err(error(cx, LayoutError::TooGeneric(ty)));
616 }
617
618 ty::Alias(..) => {
619 let err = if ty.has_param() {
622 LayoutError::TooGeneric(ty)
623 } else {
624 LayoutError::Unknown(ty)
627 };
628 return Err(error(cx, err));
629 }
630
631 ty::Bound(..) | ty::CoroutineWitness(..) | ty::Infer(_) | ty::Error(_) => {
632 bug!("layout_of: unexpected type `{ty}`")
634 }
635 })
636}
637
638fn record_layout_for_printing<'tcx>(cx: &LayoutCx<'tcx>, layout: TyAndLayout<'tcx>) {
639 if layout.ty.has_non_region_param() || !cx.typing_env.param_env.caller_bounds().is_empty() {
643 return;
644 }
645
646 let record = |kind, packed, opt_discr_size, variants| {
648 let type_desc = with_no_trimmed_paths!(format!("{}", layout.ty));
649 cx.tcx().sess.code_stats.record_type_size(
650 kind,
651 type_desc,
652 layout.align.abi,
653 layout.size,
654 packed,
655 opt_discr_size,
656 variants,
657 );
658 };
659
660 match *layout.ty.kind() {
661 ty::Adt(adt_def, _) => {
662 debug!("print-type-size t: `{:?}` process adt", layout.ty);
663 let adt_kind = adt_def.adt_kind();
664 let adt_packed = adt_def.repr().pack.is_some();
665 let (variant_infos, opt_discr_size) = variant_info_for_adt(cx, layout, adt_def);
666 record(adt_kind.into(), adt_packed, opt_discr_size, variant_infos);
667 }
668
669 ty::Coroutine(def_id, args) => {
670 debug!("print-type-size t: `{:?}` record coroutine", layout.ty);
671 let (variant_infos, opt_discr_size) =
673 variant_info_for_coroutine(cx, layout, def_id, args);
674 record(DataTypeKind::Coroutine, false, opt_discr_size, variant_infos);
675 }
676
677 ty::Closure(..) => {
678 debug!("print-type-size t: `{:?}` record closure", layout.ty);
679 record(DataTypeKind::Closure, false, None, vec![]);
680 }
681
682 _ => {
683 debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty);
684 }
685 };
686}
687
688fn variant_info_for_adt<'tcx>(
689 cx: &LayoutCx<'tcx>,
690 layout: TyAndLayout<'tcx>,
691 adt_def: AdtDef<'tcx>,
692) -> (Vec<VariantInfo>, Option<Size>) {
693 let build_variant_info = |n: Option<Symbol>, flds: &[Symbol], layout: TyAndLayout<'tcx>| {
694 let mut min_size = Size::ZERO;
695 let field_info: Vec<_> = flds
696 .iter()
697 .enumerate()
698 .map(|(i, &name)| {
699 let field_layout = layout.field(cx, i);
700 let offset = layout.fields.offset(i);
701 min_size = min_size.max(offset + field_layout.size);
702 FieldInfo {
703 kind: FieldKind::AdtField,
704 name,
705 offset: offset.bytes(),
706 size: field_layout.size.bytes(),
707 align: field_layout.align.abi.bytes(),
708 type_name: None,
709 }
710 })
711 .collect();
712
713 VariantInfo {
714 name: n,
715 kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact },
716 align: layout.align.abi.bytes(),
717 size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() },
718 fields: field_info,
719 }
720 };
721
722 match layout.variants {
723 Variants::Empty => (vec![], None),
724
725 Variants::Single { index } => {
726 debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name);
727 let variant_def = &adt_def.variant(index);
728 let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect();
729 (vec![build_variant_info(Some(variant_def.name), &fields, layout)], None)
730 }
731
732 Variants::Multiple { tag, ref tag_encoding, .. } => {
733 debug!(
734 "print-type-size `{:#?}` adt general variants def {}",
735 layout.ty,
736 adt_def.variants().len()
737 );
738 let variant_infos: Vec<_> = adt_def
739 .variants()
740 .iter_enumerated()
741 .map(|(i, variant_def)| {
742 let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect();
743 build_variant_info(Some(variant_def.name), &fields, layout.for_variant(cx, i))
744 })
745 .collect();
746
747 (
748 variant_infos,
749 match tag_encoding {
750 TagEncoding::Direct => Some(tag.size(cx)),
751 _ => None,
752 },
753 )
754 }
755 }
756}
757
758fn variant_info_for_coroutine<'tcx>(
759 cx: &LayoutCx<'tcx>,
760 layout: TyAndLayout<'tcx>,
761 def_id: DefId,
762 args: ty::GenericArgsRef<'tcx>,
763) -> (Vec<VariantInfo>, Option<Size>) {
764 use itertools::Itertools;
765
766 let Variants::Multiple { tag, ref tag_encoding, tag_field, .. } = layout.variants else {
767 return (vec![], None);
768 };
769
770 let coroutine = cx.tcx().coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap();
771 let upvar_names = cx.tcx().closure_saved_names_of_captured_variables(def_id);
772
773 let mut upvars_size = Size::ZERO;
774 let upvar_fields: Vec<_> = args
775 .as_coroutine()
776 .upvar_tys()
777 .iter()
778 .zip_eq(upvar_names)
779 .enumerate()
780 .map(|(field_idx, (_, name))| {
781 let field_layout = layout.field(cx, field_idx);
782 let offset = layout.fields.offset(field_idx);
783 upvars_size = upvars_size.max(offset + field_layout.size);
784 FieldInfo {
785 kind: FieldKind::Upvar,
786 name: *name,
787 offset: offset.bytes(),
788 size: field_layout.size.bytes(),
789 align: field_layout.align.abi.bytes(),
790 type_name: None,
791 }
792 })
793 .collect();
794
795 let mut variant_infos: Vec<_> = coroutine
796 .variant_fields
797 .iter_enumerated()
798 .map(|(variant_idx, variant_def)| {
799 let variant_layout = layout.for_variant(cx, variant_idx);
800 let mut variant_size = Size::ZERO;
801 let fields = variant_def
802 .iter()
803 .enumerate()
804 .map(|(field_idx, local)| {
805 let field_name = coroutine.field_names[*local];
806 let field_layout = variant_layout.field(cx, field_idx);
807 let offset = variant_layout.fields.offset(field_idx);
808 variant_size = variant_size.max(offset + field_layout.size);
810 FieldInfo {
811 kind: FieldKind::CoroutineLocal,
812 name: field_name.unwrap_or(Symbol::intern(&format!(
813 ".coroutine_field{}",
814 local.as_usize()
815 ))),
816 offset: offset.bytes(),
817 size: field_layout.size.bytes(),
818 align: field_layout.align.abi.bytes(),
819 type_name: (field_name.is_none() || field_name == Some(sym::__awaitee))
822 .then(|| Symbol::intern(&field_layout.ty.to_string())),
823 }
824 })
825 .chain(upvar_fields.iter().copied())
826 .collect();
827
828 if variant_size == Size::ZERO {
830 variant_size = upvars_size;
831 }
832
833 if layout.fields.offset(tag_field) >= variant_size {
849 variant_size += match tag_encoding {
850 TagEncoding::Direct => tag.size(cx),
851 _ => Size::ZERO,
852 };
853 }
854
855 VariantInfo {
856 name: Some(Symbol::intern(&ty::CoroutineArgs::variant_name(variant_idx))),
857 kind: SizeKind::Exact,
858 size: variant_size.bytes(),
859 align: variant_layout.align.abi.bytes(),
860 fields,
861 }
862 })
863 .collect();
864
865 let end_states = variant_infos.drain(1..=2);
870 let end_states: Vec<_> = end_states.collect();
871 variant_infos.extend(end_states);
872
873 (
874 variant_infos,
875 match tag_encoding {
876 TagEncoding::Direct => Some(tag.size(cx)),
877 _ => None,
878 },
879 )
880}