Skip to main content

rustdoc/html/render/
type_layout.rs

1use std::fmt;
2
3use askama::Template;
4use rustc_abi::{Primitive, TagEncoding, Variants};
5use rustc_hir::def_id::DefId;
6use rustc_middle::ty::layout::LayoutError;
7use rustc_middle::{span_bug, ty};
8use rustc_span::symbol::Symbol;
9
10use crate::html::render::Context;
11
12#[derive(Template)]
13#[template(path = "type_layout.html")]
14struct TypeLayout<'cx> {
15    variants: Vec<(Symbol, TypeLayoutSize)>,
16    type_layout_size: Result<TypeLayoutSize, &'cx LayoutError<'cx>>,
17}
18
19#[derive(Template)]
20#[template(path = "type_layout_size.html")]
21struct TypeLayoutSize {
22    is_unsized: bool,
23    is_uninhabited: bool,
24    size: u64,
25}
26
27pub(crate) fn document_type_layout(cx: &Context<'_>, ty_def_id: DefId) -> impl fmt::Display {
28    fmt::from_fn(move |f| {
29        if !cx.shared.show_type_layout {
30            return Ok(());
31        }
32
33        let tcx = cx.tcx();
34        let typing_env = ty::TypingEnv::post_analysis(tcx, ty_def_id);
35        let ty = tcx.type_of(ty_def_id).instantiate_identity();
36        let type_layout = tcx.layout_of(typing_env.as_query_input(ty));
37
38        let variants = if let Ok(type_layout) = type_layout
39            && let Variants::Multiple { variants, tag, tag_encoding, .. } =
40                type_layout.layout.variants()
41            && !variants.is_empty()
42        {
43            let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
44                0
45            } else if let Primitive::Int(i, _) = tag.primitive() {
46                i.size().bytes()
47            } else {
48                span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
49            };
50            variants
51                .iter_enumerated()
52                .map(|(variant_idx, variant_layout)| {
53                    let ty::Adt(adt, _) = type_layout.ty.kind() else {
54                        span_bug!(tcx.def_span(ty_def_id), "not an adt")
55                    };
56                    let name = adt.variant(variant_idx).name;
57                    let is_unsized = variant_layout.is_unsized();
58                    let is_uninhabited = variant_layout.is_uninhabited();
59                    let size = variant_layout.size.bytes() - tag_size;
60                    let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size };
61                    (name, type_layout_size)
62                })
63                .collect()
64        } else {
65            Vec::new()
66        };
67
68        let type_layout_size = tcx.layout_of(typing_env.as_query_input(ty)).map(|layout| {
69            let is_unsized = layout.is_unsized();
70            let is_uninhabited = layout.is_uninhabited();
71            let size = layout.size.bytes();
72            TypeLayoutSize { is_unsized, is_uninhabited, size }
73        });
74
75        TypeLayout { variants, type_layout_size }.render_into(f).unwrap();
76        Ok(())
77    })
78}