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_hir::attrs::AttributeKind;
11use rustc_hir::find_attr;
12use rustc_index::IndexVec;
13use rustc_middle::bug;
14use rustc_middle::query::Providers;
15use rustc_middle::traits::ObligationCause;
16use rustc_middle::ty::layout::{
17    FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, SimdLayoutError, TyAndLayout,
18};
19use rustc_middle::ty::print::with_no_trimmed_paths;
20use rustc_middle::ty::{
21    self, AdtDef, CoroutineArgsExt, EarlyBinder, PseudoCanonicalInput, Ty, TyCtxt, TypeVisitableExt,
22};
23use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
24use rustc_span::{Symbol, sym};
25use tracing::{debug, instrument};
26use {rustc_abi as abi, rustc_hir as hir};
27
28use crate::errors::NonPrimitiveSimdType;
29
30mod invariant;
31
32pub(crate) fn provide(providers: &mut Providers) {
33    *providers = Providers { layout_of, ..*providers };
34}
35
36#[instrument(skip(tcx, query), level = "debug")]
37fn layout_of<'tcx>(
38    tcx: TyCtxt<'tcx>,
39    query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
40) -> Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>> {
41    let PseudoCanonicalInput { typing_env, value: ty } = query;
42    debug!(?ty);
43
44    // Optimization: We convert to TypingMode::PostAnalysis and convert opaque types in
45    // the where bounds to their hidden types. This reduces overall uncached invocations
46    // of `layout_of` and is thus a small performance improvement.
47    let typing_env = typing_env.with_post_analysis_normalized(tcx);
48    let unnormalized_ty = ty;
49
50    // FIXME: We might want to have two different versions of `layout_of`:
51    // One that can be called after typecheck has completed and can use
52    // `normalize_erasing_regions` here and another one that can be called
53    // before typecheck has completed and uses `try_normalize_erasing_regions`.
54    let ty = match tcx.try_normalize_erasing_regions(typing_env, ty) {
55        Ok(t) => t,
56        Err(normalization_error) => {
57            return Err(tcx
58                .arena
59                .alloc(LayoutError::NormalizationFailure(ty, normalization_error)));
60        }
61    };
62
63    if ty != unnormalized_ty {
64        // Ensure this layout is also cached for the normalized type.
65        return tcx.layout_of(typing_env.as_query_input(ty));
66    }
67
68    let cx = LayoutCx::new(tcx, typing_env);
69
70    let layout = layout_of_uncached(&cx, ty)?;
71    let layout = TyAndLayout { ty, layout };
72
73    // If we are running with `-Zprint-type-sizes`, maybe record layouts
74    // for dumping later.
75    if cx.tcx().sess.opts.unstable_opts.print_type_sizes {
76        record_layout_for_printing(&cx, layout);
77    }
78
79    invariant::layout_sanity_check(&cx, &layout);
80
81    Ok(layout)
82}
83
84fn error<'tcx>(cx: &LayoutCx<'tcx>, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> {
85    cx.tcx().arena.alloc(err)
86}
87
88fn map_error<'tcx>(
89    cx: &LayoutCx<'tcx>,
90    ty: Ty<'tcx>,
91    err: LayoutCalculatorError<TyAndLayout<'tcx>>,
92) -> &'tcx LayoutError<'tcx> {
93    let err = match err {
94        LayoutCalculatorError::SizeOverflow => {
95            // This is sometimes not a compile error in `check` builds.
96            // See `tests/ui/limits/huge-enum.rs` for an example.
97            LayoutError::SizeOverflow(ty)
98        }
99        LayoutCalculatorError::UnexpectedUnsized(field) => {
100            // This is sometimes not a compile error if there are trivially false where clauses.
101            // See `tests/ui/layout/trivial-bounds-sized.rs` for an example.
102            assert!(field.layout.is_unsized(), "invalid layout error {err:#?}");
103            if cx.typing_env.param_env.caller_bounds().is_empty() {
104                cx.tcx().dcx().delayed_bug(format!(
105                    "encountered unexpected unsized field in layout of {ty:?}: {field:#?}"
106                ));
107            }
108            LayoutError::Unknown(ty)
109        }
110        LayoutCalculatorError::EmptyUnion => {
111            // This is always a compile error.
112            let guar =
113                cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}"));
114            LayoutError::ReferencesError(guar)
115        }
116        LayoutCalculatorError::ReprConflict => {
117            // packed enums are the only known trigger of this, but others might arise
118            let guar = cx
119                .tcx()
120                .dcx()
121                .delayed_bug(format!("computed impossible repr (packed enum?): {ty:?}"));
122            LayoutError::ReferencesError(guar)
123        }
124        LayoutCalculatorError::ZeroLengthSimdType => {
125            // Can't be caught in typeck if the array length is generic.
126            LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength }
127        }
128        LayoutCalculatorError::OversizedSimdType { max_lanes } => {
129            // Can't be caught in typeck if the array length is generic.
130            LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) }
131        }
132        LayoutCalculatorError::NonPrimitiveSimdType(field) => {
133            // This error isn't caught in typeck, e.g., if
134            // the element type of the vector is generic.
135            cx.tcx().dcx().emit_fatal(NonPrimitiveSimdType { ty, e_ty: field.ty })
136        }
137    };
138    error(cx, err)
139}
140
141fn extract_const_value<'tcx>(
142    cx: &LayoutCx<'tcx>,
143    ty: Ty<'tcx>,
144    ct: ty::Const<'tcx>,
145) -> Result<ty::Value<'tcx>, &'tcx LayoutError<'tcx>> {
146    match ct.kind() {
147        ty::ConstKind::Value(cv) => Ok(cv),
148        ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) => {
149            if !ct.has_param() {
150                bug!("failed to normalize const, but it is not generic: {ct:?}");
151            }
152            Err(error(cx, LayoutError::TooGeneric(ty)))
153        }
154        ty::ConstKind::Unevaluated(_) => {
155            let err = if ct.has_param() {
156                LayoutError::TooGeneric(ty)
157            } else {
158                // This case is reachable with unsatisfiable predicates and GCE (which will
159                // cause anon consts to inherit the unsatisfiable predicates). For example
160                // if we have an unsatisfiable `u8: Trait` bound, then it's not a compile
161                // error to mention `[u8; <u8 as Trait>::CONST]`, but we can't compute its
162                // layout.
163                LayoutError::Unknown(ty)
164            };
165            Err(error(cx, err))
166        }
167        ty::ConstKind::Infer(_)
168        | ty::ConstKind::Bound(..)
169        | ty::ConstKind::Placeholder(_)
170        | ty::ConstKind::Error(_) => {
171            // `ty::ConstKind::Error` is handled at the top of `layout_of_uncached`
172            // (via `ty.error_reported()`).
173            bug!("layout_of: unexpected const: {ct:?}");
174        }
175    }
176}
177
178fn layout_of_uncached<'tcx>(
179    cx: &LayoutCx<'tcx>,
180    ty: Ty<'tcx>,
181) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
182    // Types that reference `ty::Error` pessimistically don't have a meaningful layout.
183    // The only side-effect of this is possibly worse diagnostics in case the layout
184    // was actually computable (like if the `ty::Error` showed up only in a `PhantomData`).
185    if let Err(guar) = ty.error_reported() {
186        return Err(error(cx, LayoutError::ReferencesError(guar)));
187    }
188
189    let tcx = cx.tcx();
190
191    // layout of `async_drop_in_place<T>::{closure}` in case,
192    // when T is a coroutine, contains this internal coroutine's ref
193
194    let dl = cx.data_layout();
195    let map_layout = |result: Result<_, _>| match result {
196        Ok(layout) => Ok(tcx.mk_layout(layout)),
197        Err(err) => Err(map_error(cx, ty, err)),
198    };
199    let scalar_unit = |value: Primitive| {
200        let size = value.size(dl);
201        assert!(size.bits() <= 128);
202        Scalar::Initialized { value, valid_range: WrappingRange::full(size) }
203    };
204    let scalar = |value: Primitive| tcx.mk_layout(LayoutData::scalar(cx, scalar_unit(value)));
205
206    let univariant = |tys: &[Ty<'tcx>], kind| {
207        let fields = tys.iter().map(|ty| cx.layout_of(*ty)).try_collect::<IndexVec<_, _>>()?;
208        let repr = ReprOptions::default();
209        map_layout(cx.calc.univariant(&fields, &repr, kind))
210    };
211    debug_assert!(!ty.has_non_region_infer());
212
213    Ok(match *ty.kind() {
214        ty::Pat(ty, pat) => {
215            let layout = cx.layout_of(ty)?.layout;
216            let mut layout = LayoutData::clone(&layout.0);
217            match *pat {
218                ty::PatternKind::Range { start, end } => {
219                    if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
220                        &mut layout.backend_repr
221                    {
222                        scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
223                            .try_to_bits(tcx, cx.typing_env)
224                            .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
225
226                        scalar.valid_range_mut().end = extract_const_value(cx, ty, end)?
227                            .try_to_bits(tcx, cx.typing_env)
228                            .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
229
230                        // FIXME(pattern_types): create implied bounds from pattern types in signatures
231                        // that require that the range end is >= the range start so that we can't hit
232                        // this error anymore without first having hit a trait solver error.
233                        // Very fuzzy on the details here, but pattern types are an internal impl detail,
234                        // so we can just go with this for now
235                        if scalar.is_signed() {
236                            let range = scalar.valid_range_mut();
237                            let start = layout.size.sign_extend(range.start);
238                            let end = layout.size.sign_extend(range.end);
239                            if end < start {
240                                let guar = tcx.dcx().err(format!(
241                                    "pattern type ranges cannot wrap: {start}..={end}"
242                                ));
243
244                                return Err(error(cx, LayoutError::ReferencesError(guar)));
245                            }
246                        } else {
247                            let range = scalar.valid_range_mut();
248                            if range.end < range.start {
249                                let guar = tcx.dcx().err(format!(
250                                    "pattern type ranges cannot wrap: {}..={}",
251                                    range.start, range.end
252                                ));
253
254                                return Err(error(cx, LayoutError::ReferencesError(guar)));
255                            }
256                        };
257
258                        let niche = Niche {
259                            offset: Size::ZERO,
260                            value: scalar.primitive(),
261                            valid_range: scalar.valid_range(cx),
262                        };
263
264                        layout.largest_niche = Some(niche);
265                    } else {
266                        bug!("pattern type with range but not scalar layout: {ty:?}, {layout:?}")
267                    }
268                }
269                ty::PatternKind::Or(variants) => match *variants[0] {
270                    ty::PatternKind::Range { .. } => {
271                        if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
272                            let variants: Result<Vec<_>, _> = variants
273                                .iter()
274                                .map(|pat| match *pat {
275                                    ty::PatternKind::Range { start, end } => Ok((
276                                        extract_const_value(cx, ty, start)
277                                            .unwrap()
278                                            .try_to_bits(tcx, cx.typing_env)
279                                            .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?,
280                                        extract_const_value(cx, ty, end)
281                                            .unwrap()
282                                            .try_to_bits(tcx, cx.typing_env)
283                                            .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?,
284                                    )),
285                                    ty::PatternKind::Or(_) => {
286                                        unreachable!("mixed or patterns are not allowed")
287                                    }
288                                })
289                                .collect();
290                            let mut variants = variants?;
291                            if !scalar.is_signed() {
292                                let guar = tcx.dcx().err(format!(
293                                    "only signed integer base types are allowed for or-pattern pattern types at present"
294                                ));
295
296                                return Err(error(cx, LayoutError::ReferencesError(guar)));
297                            }
298                            variants.sort();
299                            if variants.len() != 2 {
300                                let guar = tcx
301                                .dcx()
302                                .err(format!("the only or-pattern types allowed are two range patterns that are directly connected at their overflow site"));
303
304                                return Err(error(cx, LayoutError::ReferencesError(guar)));
305                            }
306
307                            // first is the one starting at the signed in range min
308                            let mut first = variants[0];
309                            let mut second = variants[1];
310                            if second.0
311                                == layout.size.truncate(layout.size.signed_int_min() as u128)
312                            {
313                                (second, first) = (first, second);
314                            }
315
316                            if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0)
317                            {
318                                let guar = tcx.dcx().err(format!(
319                                    "only non-overlapping pattern type ranges are allowed at present"
320                                ));
321
322                                return Err(error(cx, LayoutError::ReferencesError(guar)));
323                            }
324                            if layout.size.signed_int_max() as u128 != second.1 {
325                                let guar = tcx.dcx().err(format!(
326                                    "one pattern needs to end at `{ty}::MAX`, but was {} instead",
327                                    second.1
328                                ));
329
330                                return Err(error(cx, LayoutError::ReferencesError(guar)));
331                            }
332
333                            // Now generate a wrapping range (which aren't allowed in surface syntax).
334                            scalar.valid_range_mut().start = second.0;
335                            scalar.valid_range_mut().end = first.1;
336
337                            let niche = Niche {
338                                offset: Size::ZERO,
339                                value: scalar.primitive(),
340                                valid_range: scalar.valid_range(cx),
341                            };
342
343                            layout.largest_niche = Some(niche);
344                        } else {
345                            bug!(
346                                "pattern type with range but not scalar layout: {ty:?}, {layout:?}"
347                            )
348                        }
349                    }
350                    ty::PatternKind::Or(..) => bug!("patterns cannot have nested or patterns"),
351                },
352            }
353            tcx.mk_layout(layout)
354        }
355
356        // Basic scalars.
357        ty::Bool => tcx.mk_layout(LayoutData::scalar(
358            cx,
359            Scalar::Initialized {
360                value: Int(I8, false),
361                valid_range: WrappingRange { start: 0, end: 1 },
362            },
363        )),
364        ty::Char => tcx.mk_layout(LayoutData::scalar(
365            cx,
366            Scalar::Initialized {
367                value: Int(I32, false),
368                valid_range: WrappingRange { start: 0, end: 0x10FFFF },
369            },
370        )),
371        ty::Int(ity) => scalar(Int(abi::Integer::from_int_ty(dl, ity), true)),
372        ty::Uint(ity) => scalar(Int(abi::Integer::from_uint_ty(dl, ity), false)),
373        ty::Float(fty) => scalar(Float(abi::Float::from_float_ty(fty))),
374        ty::FnPtr(..) => {
375            let mut ptr = scalar_unit(Pointer(dl.instruction_address_space));
376            ptr.valid_range_mut().start = 1;
377            tcx.mk_layout(LayoutData::scalar(cx, ptr))
378        }
379
380        // The never type.
381        ty::Never => tcx.mk_layout(LayoutData::never_type(cx)),
382
383        // Potentially-wide pointers.
384        ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => {
385            let mut data_ptr = scalar_unit(Pointer(AddressSpace::ZERO));
386            if !ty.is_raw_ptr() {
387                data_ptr.valid_range_mut().start = 1;
388            }
389
390            if pointee.is_sized(tcx, cx.typing_env) {
391                return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr)));
392            }
393
394            let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() {
395                let pointee_metadata = Ty::new_projection(tcx, metadata_def_id, [pointee]);
396                let metadata_ty = match tcx
397                    .try_normalize_erasing_regions(cx.typing_env, pointee_metadata)
398                {
399                    Ok(metadata_ty) => metadata_ty,
400                    Err(mut err) => {
401                        // Usually `<Ty as Pointee>::Metadata` can't be normalized because
402                        // its struct tail cannot be normalized either, so try to get a
403                        // more descriptive layout error here, which will lead to less confusing
404                        // diagnostics.
405                        //
406                        // We use the raw struct tail function here to get the first tail
407                        // that is an alias, which is likely the cause of the normalization
408                        // error.
409                        match tcx.try_normalize_erasing_regions(
410                            cx.typing_env,
411                            tcx.struct_tail_raw(pointee, &ObligationCause::dummy(), |ty| ty, || {}),
412                        ) {
413                            Ok(_) => {}
414                            Err(better_err) => {
415                                err = better_err;
416                            }
417                        }
418                        return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
419                    }
420                };
421
422                let metadata_layout = cx.layout_of(metadata_ty)?;
423                // If the metadata is a 1-zst, then the pointer is thin.
424                if metadata_layout.is_1zst() {
425                    return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr)));
426                }
427
428                let BackendRepr::Scalar(metadata) = metadata_layout.backend_repr else {
429                    return Err(error(cx, LayoutError::Unknown(pointee)));
430                };
431
432                metadata
433            } else {
434                let unsized_part = tcx.struct_tail_for_codegen(pointee, cx.typing_env);
435
436                match unsized_part.kind() {
437                    ty::Foreign(..) => {
438                        return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr)));
439                    }
440                    ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
441                    ty::Dynamic(..) => {
442                        let mut vtable = scalar_unit(Pointer(AddressSpace::ZERO));
443                        vtable.valid_range_mut().start = 1;
444                        vtable
445                    }
446                    _ => {
447                        return Err(error(cx, LayoutError::Unknown(pointee)));
448                    }
449                }
450            };
451
452            // Effectively a (ptr, meta) tuple.
453            tcx.mk_layout(LayoutData::scalar_pair(cx, data_ptr, metadata))
454        }
455
456        // Arrays and slices.
457        ty::Array(element, count) => {
458            let count = extract_const_value(cx, ty, count)?
459                .try_to_target_usize(tcx)
460                .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
461
462            let element = cx.layout_of(element)?;
463            map_layout(cx.calc.array_like(&element, Some(count)))?
464        }
465        ty::Slice(element) => {
466            let element = cx.layout_of(element)?;
467            map_layout(cx.calc.array_like(&element, None).map(|mut layout| {
468                // a randomly chosen value to distinguish slices
469                layout.randomization_seed = Hash64::new(0x2dcba99c39784102);
470                layout
471            }))?
472        }
473        ty::Str => {
474            let element = scalar(Int(I8, false));
475            map_layout(cx.calc.array_like(&element, None).map(|mut layout| {
476                // another random value
477                layout.randomization_seed = Hash64::new(0xc1325f37d127be22);
478                layout
479            }))?
480        }
481
482        // Odd unit types.
483        ty::FnDef(..) | ty::Dynamic(_, _) | ty::Foreign(..) => {
484            let sized = matches!(ty.kind(), ty::FnDef(..));
485            tcx.mk_layout(LayoutData::unit(cx, sized))
486        }
487
488        ty::Coroutine(def_id, args) => {
489            use rustc_middle::ty::layout::PrimitiveExt as _;
490
491            let info = tcx.coroutine_layout(def_id, args)?;
492
493            let local_layouts = info
494                .field_tys
495                .iter()
496                .map(|local| {
497                    let field_ty = EarlyBinder::bind(local.ty);
498                    let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty.instantiate(tcx, args));
499                    cx.spanned_layout_of(uninit_ty, local.source_info.span)
500                })
501                .try_collect::<IndexVec<_, _>>()?;
502
503            let prefix_layouts = args
504                .as_coroutine()
505                .prefix_tys()
506                .iter()
507                .map(|ty| cx.layout_of(ty))
508                .try_collect::<IndexVec<_, _>>()?;
509
510            let layout = cx
511                .calc
512                .coroutine(
513                    &local_layouts,
514                    prefix_layouts,
515                    &info.variant_fields,
516                    &info.storage_conflicts,
517                    |tag| TyAndLayout {
518                        ty: tag.primitive().to_ty(tcx),
519                        layout: tcx.mk_layout(LayoutData::scalar(cx, tag)),
520                    },
521                )
522                .map(|mut layout| {
523                    // this is similar to how ReprOptions populates its field_shuffle_seed
524                    layout.randomization_seed = tcx.def_path_hash(def_id).0.to_smaller_hash();
525                    debug!("coroutine layout ({:?}): {:#?}", ty, layout);
526                    layout
527                });
528            map_layout(layout)?
529        }
530
531        ty::Closure(_, args) => univariant(args.as_closure().upvar_tys(), StructKind::AlwaysSized)?,
532
533        ty::CoroutineClosure(_, args) => {
534            univariant(args.as_coroutine_closure().upvar_tys(), StructKind::AlwaysSized)?
535        }
536
537        ty::Tuple(tys) => {
538            let kind =
539                if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
540
541            univariant(tys, kind)?
542        }
543
544        // SIMD vector types.
545        ty::Adt(def, args) if def.repr().simd() => {
546            // Supported SIMD vectors are ADTs with a single array field:
547            //
548            // * #[repr(simd)] struct S([T; 4])
549            //
550            // where T is a primitive scalar (integer/float/pointer).
551            let Some(ty::Array(e_ty, e_len)) = def
552                .is_struct()
553                .then(|| &def.variant(FIRST_VARIANT).fields)
554                .filter(|fields| fields.len() == 1)
555                .map(|fields| *fields[FieldIdx::ZERO].ty(tcx, args).kind())
556            else {
557                // Invalid SIMD types should have been caught by typeck by now.
558                let guar = tcx.dcx().delayed_bug("#[repr(simd)] was applied to an invalid ADT");
559                return Err(error(cx, LayoutError::ReferencesError(guar)));
560            };
561
562            let e_len = extract_const_value(cx, ty, e_len)?
563                .try_to_target_usize(tcx)
564                .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
565
566            let e_ly = cx.layout_of(e_ty)?;
567
568            // Check for the rustc_simd_monomorphize_lane_limit attribute and check the lane limit
569            if let Some(limit) = find_attr!(
570                tcx.get_all_attrs(def.did()),
571                AttributeKind::RustcSimdMonomorphizeLaneLimit(limit) => limit
572            ) {
573                if !limit.value_within_limit(e_len as usize) {
574                    return Err(map_error(
575                        &cx,
576                        ty,
577                        rustc_abi::LayoutCalculatorError::OversizedSimdType {
578                            max_lanes: limit.0 as u64,
579                        },
580                    ));
581                }
582            }
583
584            map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))?
585        }
586
587        // ADTs.
588        ty::Adt(def, args) => {
589            // Cache the field layouts.
590            let variants = def
591                .variants()
592                .iter()
593                .map(|v| {
594                    v.fields
595                        .iter()
596                        .map(|field| cx.layout_of(field.ty(tcx, args)))
597                        .try_collect::<IndexVec<_, _>>()
598                })
599                .try_collect::<IndexVec<VariantIdx, _>>()?;
600
601            if def.is_union() {
602                if def.repr().pack.is_some() && def.repr().align.is_some() {
603                    let guar = tcx.dcx().span_delayed_bug(
604                        tcx.def_span(def.did()),
605                        "union cannot be packed and aligned",
606                    );
607                    return Err(error(cx, LayoutError::ReferencesError(guar)));
608                }
609
610                return map_layout(cx.calc.layout_of_union(&def.repr(), &variants));
611            }
612
613            // UnsafeCell and UnsafePinned both disable niche optimizations
614            let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned();
615
616            let get_discriminant_type =
617                |min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max);
618
619            let discriminants_iter = || {
620                def.is_enum()
621                    .then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val as i128)))
622                    .into_iter()
623                    .flatten()
624            };
625
626            let maybe_unsized = def.is_struct()
627                && def.non_enum_variant().tail_opt().is_some_and(|last_field| {
628                    let typing_env = ty::TypingEnv::post_analysis(tcx, def.did());
629                    !tcx.type_of(last_field.did).instantiate_identity().is_sized(tcx, typing_env)
630                });
631
632            let layout = cx
633                .calc
634                .layout_of_struct_or_enum(
635                    &def.repr(),
636                    &variants,
637                    def.is_enum(),
638                    is_special_no_niche,
639                    tcx.layout_scalar_valid_range(def.did()),
640                    get_discriminant_type,
641                    discriminants_iter(),
642                    !maybe_unsized,
643                )
644                .map_err(|err| map_error(cx, ty, err))?;
645
646            if !maybe_unsized && layout.is_unsized() {
647                bug!("got unsized layout for type that cannot be unsized {ty:?}: {layout:#?}");
648            }
649
650            // If the struct tail is sized and can be unsized, check that unsizing doesn't move the fields around.
651            if cfg!(debug_assertions)
652                && maybe_unsized
653                && def.non_enum_variant().tail().ty(tcx, args).is_sized(tcx, cx.typing_env)
654            {
655                let mut variants = variants;
656                let tail_replacement = cx.layout_of(Ty::new_slice(tcx, tcx.types.u8)).unwrap();
657                *variants[FIRST_VARIANT].raw.last_mut().unwrap() = tail_replacement;
658
659                let Ok(unsized_layout) = cx.calc.layout_of_struct_or_enum(
660                    &def.repr(),
661                    &variants,
662                    def.is_enum(),
663                    is_special_no_niche,
664                    tcx.layout_scalar_valid_range(def.did()),
665                    get_discriminant_type,
666                    discriminants_iter(),
667                    !maybe_unsized,
668                ) else {
669                    bug!("failed to compute unsized layout of {ty:?}");
670                };
671
672                let FieldsShape::Arbitrary { offsets: sized_offsets, .. } = &layout.fields else {
673                    bug!("unexpected FieldsShape for sized layout of {ty:?}: {:?}", layout.fields);
674                };
675                let FieldsShape::Arbitrary { offsets: unsized_offsets, .. } =
676                    &unsized_layout.fields
677                else {
678                    bug!(
679                        "unexpected FieldsShape for unsized layout of {ty:?}: {:?}",
680                        unsized_layout.fields
681                    );
682                };
683
684                let (sized_tail, sized_fields) = sized_offsets.raw.split_last().unwrap();
685                let (unsized_tail, unsized_fields) = unsized_offsets.raw.split_last().unwrap();
686
687                if sized_fields != unsized_fields {
688                    bug!("unsizing {ty:?} changed field order!\n{layout:?}\n{unsized_layout:?}");
689                }
690
691                if sized_tail < unsized_tail {
692                    bug!("unsizing {ty:?} moved tail backwards!\n{layout:?}\n{unsized_layout:?}");
693                }
694            }
695
696            tcx.mk_layout(layout)
697        }
698
699        ty::UnsafeBinder(bound_ty) => {
700            let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into());
701            cx.layout_of(ty)?.layout
702        }
703
704        // Types with no meaningful known layout.
705        ty::Param(_) | ty::Placeholder(..) => {
706            return Err(error(cx, LayoutError::TooGeneric(ty)));
707        }
708
709        ty::Alias(..) => {
710            // NOTE(eddyb) `layout_of` query should've normalized these away,
711            // if that was possible, so there's no reason to try again here.
712            let err = if ty.has_param() {
713                LayoutError::TooGeneric(ty)
714            } else {
715                // This is only reachable with unsatisfiable predicates. For example, if we have
716                // `u8: Iterator`, then we can't compute the layout of `<u8 as Iterator>::Item`.
717                LayoutError::Unknown(ty)
718            };
719            return Err(error(cx, err));
720        }
721
722        ty::Bound(..) | ty::CoroutineWitness(..) | ty::Infer(_) | ty::Error(_) => {
723            // `ty::Error` is handled at the top of this function.
724            bug!("layout_of: unexpected type `{ty}`")
725        }
726    })
727}
728
729fn record_layout_for_printing<'tcx>(cx: &LayoutCx<'tcx>, layout: TyAndLayout<'tcx>) {
730    // Ignore layouts that are done with non-empty environments or
731    // non-monomorphic layouts, as the user only wants to see the stuff
732    // resulting from the final codegen session.
733    if layout.ty.has_non_region_param() || !cx.typing_env.param_env.caller_bounds().is_empty() {
734        return;
735    }
736
737    // (delay format until we actually need it)
738    let record = |kind, packed, opt_discr_size, variants| {
739        let type_desc = with_no_trimmed_paths!(format!("{}", layout.ty));
740        cx.tcx().sess.code_stats.record_type_size(
741            kind,
742            type_desc,
743            layout.align.abi,
744            layout.size,
745            packed,
746            opt_discr_size,
747            variants,
748        );
749    };
750
751    match *layout.ty.kind() {
752        ty::Adt(adt_def, _) => {
753            debug!("print-type-size t: `{:?}` process adt", layout.ty);
754            let adt_kind = adt_def.adt_kind();
755            let adt_packed = adt_def.repr().pack.is_some();
756            let (variant_infos, opt_discr_size) = variant_info_for_adt(cx, layout, adt_def);
757            record(adt_kind.into(), adt_packed, opt_discr_size, variant_infos);
758        }
759
760        ty::Coroutine(def_id, args) => {
761            debug!("print-type-size t: `{:?}` record coroutine", layout.ty);
762            // Coroutines always have a begin/poisoned/end state with additional suspend points
763            let (variant_infos, opt_discr_size) =
764                variant_info_for_coroutine(cx, layout, def_id, args);
765            record(DataTypeKind::Coroutine, false, opt_discr_size, variant_infos);
766        }
767
768        ty::Closure(..) => {
769            debug!("print-type-size t: `{:?}` record closure", layout.ty);
770            record(DataTypeKind::Closure, false, None, vec![]);
771        }
772
773        _ => {
774            debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty);
775        }
776    };
777}
778
779fn variant_info_for_adt<'tcx>(
780    cx: &LayoutCx<'tcx>,
781    layout: TyAndLayout<'tcx>,
782    adt_def: AdtDef<'tcx>,
783) -> (Vec<VariantInfo>, Option<Size>) {
784    let build_variant_info = |n: Option<Symbol>, flds: &[Symbol], layout: TyAndLayout<'tcx>| {
785        let mut min_size = Size::ZERO;
786        let field_info: Vec<_> = flds
787            .iter()
788            .enumerate()
789            .map(|(i, &name)| {
790                let field_layout = layout.field(cx, i);
791                let offset = layout.fields.offset(i);
792                min_size = min_size.max(offset + field_layout.size);
793                FieldInfo {
794                    kind: FieldKind::AdtField,
795                    name,
796                    offset: offset.bytes(),
797                    size: field_layout.size.bytes(),
798                    align: field_layout.align.bytes(),
799                    type_name: None,
800                }
801            })
802            .collect();
803
804        VariantInfo {
805            name: n,
806            kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact },
807            align: layout.align.bytes(),
808            size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() },
809            fields: field_info,
810        }
811    };
812
813    match layout.variants {
814        Variants::Empty => (vec![], None),
815
816        Variants::Single { index } => {
817            debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name);
818            let variant_def = &adt_def.variant(index);
819            let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect();
820            (vec![build_variant_info(Some(variant_def.name), &fields, layout)], None)
821        }
822
823        Variants::Multiple { tag, ref tag_encoding, .. } => {
824            debug!(
825                "print-type-size `{:#?}` adt general variants def {}",
826                layout.ty,
827                adt_def.variants().len()
828            );
829            let variant_infos: Vec<_> = adt_def
830                .variants()
831                .iter_enumerated()
832                .map(|(i, variant_def)| {
833                    let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect();
834                    build_variant_info(Some(variant_def.name), &fields, layout.for_variant(cx, i))
835                })
836                .collect();
837
838            (
839                variant_infos,
840                match tag_encoding {
841                    TagEncoding::Direct => Some(tag.size(cx)),
842                    _ => None,
843                },
844            )
845        }
846    }
847}
848
849fn variant_info_for_coroutine<'tcx>(
850    cx: &LayoutCx<'tcx>,
851    layout: TyAndLayout<'tcx>,
852    def_id: DefId,
853    args: ty::GenericArgsRef<'tcx>,
854) -> (Vec<VariantInfo>, Option<Size>) {
855    use itertools::Itertools;
856
857    let Variants::Multiple { tag, ref tag_encoding, tag_field, .. } = layout.variants else {
858        return (vec![], None);
859    };
860
861    let coroutine = cx.tcx().coroutine_layout(def_id, args).unwrap();
862    let upvar_names = cx.tcx().closure_saved_names_of_captured_variables(def_id);
863
864    let mut upvars_size = Size::ZERO;
865    let upvar_fields: Vec<_> = args
866        .as_coroutine()
867        .upvar_tys()
868        .iter()
869        .zip_eq(upvar_names)
870        .enumerate()
871        .map(|(field_idx, (_, name))| {
872            let field_layout = layout.field(cx, field_idx);
873            let offset = layout.fields.offset(field_idx);
874            upvars_size = upvars_size.max(offset + field_layout.size);
875            FieldInfo {
876                kind: FieldKind::Upvar,
877                name: *name,
878                offset: offset.bytes(),
879                size: field_layout.size.bytes(),
880                align: field_layout.align.bytes(),
881                type_name: None,
882            }
883        })
884        .collect();
885
886    let mut variant_infos: Vec<_> = coroutine
887        .variant_fields
888        .iter_enumerated()
889        .map(|(variant_idx, variant_def)| {
890            let variant_layout = layout.for_variant(cx, variant_idx);
891            let mut variant_size = Size::ZERO;
892            let fields = variant_def
893                .iter()
894                .enumerate()
895                .map(|(field_idx, local)| {
896                    let field_name = coroutine.field_names[*local];
897                    let field_layout = variant_layout.field(cx, field_idx);
898                    let offset = variant_layout.fields.offset(field_idx);
899                    // The struct is as large as the last field's end
900                    variant_size = variant_size.max(offset + field_layout.size);
901                    FieldInfo {
902                        kind: FieldKind::CoroutineLocal,
903                        name: field_name.unwrap_or_else(|| {
904                            Symbol::intern(&format!(".coroutine_field{}", local.as_usize()))
905                        }),
906                        offset: offset.bytes(),
907                        size: field_layout.size.bytes(),
908                        align: field_layout.align.bytes(),
909                        // Include the type name if there is no field name, or if the name is the
910                        // __awaitee placeholder symbol which means a child future being `.await`ed.
911                        type_name: (field_name.is_none() || field_name == Some(sym::__awaitee))
912                            .then(|| Symbol::intern(&field_layout.ty.to_string())),
913                    }
914                })
915                .chain(upvar_fields.iter().copied())
916                .collect();
917
918            // If the variant has no state-specific fields, then it's the size of the upvars.
919            if variant_size == Size::ZERO {
920                variant_size = upvars_size;
921            }
922
923            // This `if` deserves some explanation.
924            //
925            // The layout code has a choice of where to place the discriminant of this coroutine.
926            // If the discriminant of the coroutine is placed early in the layout (before the
927            // variant's own fields), then it'll implicitly be counted towards the size of the
928            // variant, since we use the maximum offset to calculate size.
929            //    (side-note: I know this is a bit problematic given upvars placement, etc).
930            //
931            // This is important, since the layout printing code always subtracts this discriminant
932            // size from the variant size if the struct is "enum"-like, so failing to account for it
933            // will either lead to numerical underflow, or an underreported variant size...
934            //
935            // However, if the discriminant is placed past the end of the variant, then we need
936            // to factor in the size of the discriminant manually. This really should be refactored
937            // better, but this "works" for now.
938            if layout.fields.offset(tag_field.as_usize()) >= variant_size {
939                variant_size += match tag_encoding {
940                    TagEncoding::Direct => tag.size(cx),
941                    _ => Size::ZERO,
942                };
943            }
944
945            VariantInfo {
946                name: Some(Symbol::intern(&ty::CoroutineArgs::variant_name(variant_idx))),
947                kind: SizeKind::Exact,
948                size: variant_size.bytes(),
949                align: variant_layout.align.bytes(),
950                fields,
951            }
952        })
953        .collect();
954
955    // The first three variants are hardcoded to be `UNRESUMED`, `RETURNED` and `POISONED`.
956    // We will move the `RETURNED` and `POISONED` elements to the end so we
957    // are left with a sorting order according to the coroutines yield points:
958    // First `Unresumed`, then the `SuspendN` followed by `Returned` and `Panicked` (POISONED).
959    let end_states = variant_infos.drain(1..=2);
960    let end_states: Vec<_> = end_states.collect();
961    variant_infos.extend(end_states);
962
963    (
964        variant_infos,
965        match tag_encoding {
966            TagEncoding::Direct => Some(tag.size(cx)),
967            _ => None,
968        },
969    )
970}