rustc_ty_utils/
layout.rs

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    // Optimization: We convert to TypingMode::PostAnalysis and convert opaque types in
42    // the where bounds to their hidden types. This reduces overall uncached invocations
43    // of `layout_of` and is thus a small performance improvement.
44    let typing_env = typing_env.with_post_analysis_normalized(tcx);
45    let unnormalized_ty = ty;
46
47    // FIXME: We might want to have two different versions of `layout_of`:
48    // One that can be called after typecheck has completed and can use
49    // `normalize_erasing_regions` here and another one that can be called
50    // before typecheck has completed and uses `try_normalize_erasing_regions`.
51    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        // Ensure this layout is also cached for the normalized type.
62        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 we are running with `-Zprint-type-sizes`, maybe record layouts
71    // for dumping later.
72    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            // This is sometimes not a compile error in `check` builds.
93            // See `tests/ui/limits/huge-enum.rs` for an example.
94            LayoutError::SizeOverflow(ty)
95        }
96        LayoutCalculatorError::UnexpectedUnsized(field) => {
97            // This is sometimes not a compile error if there are trivially false where clauses.
98            // See `tests/ui/layout/trivial-bounds-sized.rs` for an example.
99            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            // This is always a compile error.
109            let guar =
110                cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}"));
111            LayoutError::ReferencesError(guar)
112        }
113        LayoutCalculatorError::ReprConflict => {
114            // packed enums are the only known trigger of this, but others might arise
115            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            // Can't be caught in typeck if the array length is generic.
123            cx.tcx().dcx().emit_fatal(ZeroLengthSimdType { ty })
124        }
125        LayoutCalculatorError::OversizedSimdType { max_lanes } => {
126            // Can't be caught in typeck if the array length is generic.
127            cx.tcx().dcx().emit_fatal(OversizedSimdType { ty, max_lanes })
128        }
129        LayoutCalculatorError::NonPrimitiveSimdType(field) => {
130            // This error isn't caught in typeck, e.g., if
131            // the element type of the vector is generic.
132            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                // This case is reachable with unsatisfiable predicates and GCE (which will
156                // cause anon consts to inherit the unsatisfiable predicates). For example
157                // if we have an unsatisfiable `u8: Trait` bound, then it's not a compile
158                // error to mention `[u8; <u8 as Trait>::CONST]`, but we can't compute its
159                // layout.
160                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            // `ty::ConstKind::Error` is handled at the top of `layout_of_uncached`
169            // (via `ty.error_reported()`).
170            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    // Types that reference `ty::Error` pessimistically don't have a meaningful layout.
180    // The only side-effect of this is possibly worse diagnostics in case the layout
181    // was actually computable (like if the `ty::Error` showed up only in a `PhantomData`).
182    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                        // FIXME(pattern_types): create implied bounds from pattern types in signatures
224                        // that require that the range end is >= the range start so that we can't hit
225                        // this error anymore without first having hit a trait solver error.
226                        // Very fuzzy on the details here, but pattern types are an internal impl detail,
227                        // so we can just go with this for now
228                        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        // Basic scalars.
268        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        // The never type.
292        ty::Never => tcx.mk_layout(LayoutData::never_type(cx)),
293
294        // Potentially-wide pointers.
295        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                            // Usually `<Ty as Pointee>::Metadata` can't be normalized because
312                            // its struct tail cannot be normalized either, so try to get a
313                            // more descriptive layout error here, which will lead to less confusing
314                            // diagnostics.
315                            //
316                            // We use the raw struct tail function here to get the first tail
317                            // that is an alias, which is likely the cause of the normalization
318                            // error.
319                            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 the metadata is a 1-zst, then the pointer is thin.
334                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            // Effectively a (ptr, meta) tuple.
363            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        // Arrays and slices.
375        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                // a randomly chosen value to distinguish slices
387                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                // another random value
395                layout.randomization_seed = Hash64::new(0xc1325f37d127be22);
396                layout
397            }))?
398        }
399
400        // Odd unit types.
401        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                    // this is similar to how ReprOptions populates its field_shuffle_seed
444                    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        // SIMD vector types.
465        ty::Adt(def, args) if def.repr().simd() => {
466            // Supported SIMD vectors are ADTs with a single array field:
467            //
468            // * #[repr(simd)] struct S([T; 4])
469            //
470            // where T is a primitive scalar (integer/float/pointer).
471            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                // Invalid SIMD types should have been caught by typeck by now.
478                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        // ADTs.
492        ty::Adt(def, args) => {
493            // Cache the field layouts.
494            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 the struct tail is sized and can be unsized, check that unsizing doesn't move the fields around.
559            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        // Types with no meaningful known layout.
614        ty::Param(_) | ty::Placeholder(..) => {
615            return Err(error(cx, LayoutError::TooGeneric(ty)));
616        }
617
618        ty::Alias(..) => {
619            // NOTE(eddyb) `layout_of` query should've normalized these away,
620            // if that was possible, so there's no reason to try again here.
621            let err = if ty.has_param() {
622                LayoutError::TooGeneric(ty)
623            } else {
624                // This is only reachable with unsatisfiable predicates. For example, if we have
625                // `u8: Iterator`, then we can't compute the layout of `<u8 as Iterator>::Item`.
626                LayoutError::Unknown(ty)
627            };
628            return Err(error(cx, err));
629        }
630
631        ty::Bound(..) | ty::CoroutineWitness(..) | ty::Infer(_) | ty::Error(_) => {
632            // `ty::Error` is handled at the top of this function.
633            bug!("layout_of: unexpected type `{ty}`")
634        }
635    })
636}
637
638fn record_layout_for_printing<'tcx>(cx: &LayoutCx<'tcx>, layout: TyAndLayout<'tcx>) {
639    // Ignore layouts that are done with non-empty environments or
640    // non-monomorphic layouts, as the user only wants to see the stuff
641    // resulting from the final codegen session.
642    if layout.ty.has_non_region_param() || !cx.typing_env.param_env.caller_bounds().is_empty() {
643        return;
644    }
645
646    // (delay format until we actually need it)
647    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            // Coroutines always have a begin/poisoned/end state with additional suspend points
672            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                    // The struct is as large as the last field's end
809                    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                        // Include the type name if there is no field name, or if the name is the
820                        // __awaitee placeholder symbol which means a child future being `.await`ed.
821                        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 the variant has no state-specific fields, then it's the size of the upvars.
829            if variant_size == Size::ZERO {
830                variant_size = upvars_size;
831            }
832
833            // This `if` deserves some explanation.
834            //
835            // The layout code has a choice of where to place the discriminant of this coroutine.
836            // If the discriminant of the coroutine is placed early in the layout (before the
837            // variant's own fields), then it'll implicitly be counted towards the size of the
838            // variant, since we use the maximum offset to calculate size.
839            //    (side-note: I know this is a bit problematic given upvars placement, etc).
840            //
841            // This is important, since the layout printing code always subtracts this discriminant
842            // size from the variant size if the struct is "enum"-like, so failing to account for it
843            // will either lead to numerical underflow, or an underreported variant size...
844            //
845            // However, if the discriminant is placed past the end of the variant, then we need
846            // to factor in the size of the discriminant manually. This really should be refactored
847            // better, but this "works" for now.
848            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    // The first three variants are hardcoded to be `UNRESUMED`, `RETURNED` and `POISONED`.
866    // We will move the `RETURNED` and `POISONED` elements to the end so we
867    // are left with a sorting order according to the coroutines yield points:
868    // First `Unresumed`, then the `SuspendN` followed by `Returned` and `Panicked` (POISONED).
869    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}