1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use std::fmt;

use rinja::Template;
use rustc_data_structures::captures::Captures;
use rustc_hir::def_id::DefId;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::{self};
use rustc_span::symbol::Symbol;
use rustc_target::abi::{Primitive, TagEncoding, Variants};

use crate::html::format::display_fn;
use crate::html::render::Context;

#[derive(Template)]
#[template(path = "type_layout.html")]
struct TypeLayout<'cx> {
    variants: Vec<(Symbol, TypeLayoutSize)>,
    type_layout_size: Result<TypeLayoutSize, &'cx LayoutError<'cx>>,
}

#[derive(Template)]
#[template(path = "type_layout_size.html")]
struct TypeLayoutSize {
    is_unsized: bool,
    is_uninhabited: bool,
    size: u64,
}

pub(crate) fn document_type_layout<'a, 'cx: 'a>(
    cx: &'a Context<'cx>,
    ty_def_id: DefId,
) -> impl fmt::Display + 'a + Captures<'cx> {
    display_fn(move |f| {
        if !cx.shared.show_type_layout {
            return Ok(());
        }

        let tcx = cx.tcx();
        let param_env = tcx.param_env(ty_def_id);
        let ty = tcx.type_of(ty_def_id).instantiate_identity();
        let type_layout = tcx.layout_of(param_env.and(ty));

        let variants = if let Ok(type_layout) = type_layout
            && let Variants::Multiple { variants, tag, tag_encoding, .. } =
                type_layout.layout.variants()
            && !variants.is_empty()
        {
            let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
                0
            } else if let Primitive::Int(i, _) = tag.primitive() {
                i.size().bytes()
            } else {
                span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
            };
            variants
                .iter_enumerated()
                .map(|(variant_idx, variant_layout)| {
                    let ty::Adt(adt, _) = type_layout.ty.kind() else {
                        span_bug!(tcx.def_span(ty_def_id), "not an adt")
                    };
                    let name = adt.variant(variant_idx).name;
                    let is_unsized = variant_layout.abi.is_unsized();
                    let is_uninhabited = variant_layout.abi.is_uninhabited();
                    let size = variant_layout.size.bytes() - tag_size;
                    let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size };
                    (name, type_layout_size)
                })
                .collect()
        } else {
            Vec::new()
        };

        let type_layout_size = tcx.layout_of(param_env.and(ty)).map(|layout| {
            let is_unsized = layout.abi.is_unsized();
            let is_uninhabited = layout.abi.is_uninhabited();
            let size = layout.size.bytes();
            TypeLayoutSize { is_unsized, is_uninhabited, size }
        });

        TypeLayout { variants, type_layout_size }.render_into(f).unwrap();
        Ok(())
    })
}