Skip to content

Commit 40be1d3

Browse files
authored
Rollup merge of #83501 - camelid:rustdoc-layout, r=jyn514,GuillaumeGomez
rustdoc: Add unstable CLI option to show basic type layout information Closes #75988. Right now it just shows the size.
2 parents 5c02926 + d43701c commit 40be1d3

File tree

7 files changed

+145
-4
lines changed

7 files changed

+145
-4
lines changed

src/bootstrap/doc.rs

+2
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ impl Step for Rustc {
549549
cargo.rustdocflag("--enable-index-page");
550550
cargo.rustdocflag("-Zunstable-options");
551551
cargo.rustdocflag("-Znormalize-docs");
552+
cargo.rustdocflag("--show-type-layout");
552553
compile::rustc_cargo(builder, &mut cargo, target);
553554

554555
// Only include compiler crates, no dependencies of those, such as `libc`.
@@ -648,6 +649,7 @@ impl Step for Rustdoc {
648649

649650
cargo.rustdocflag("--document-private-items");
650651
cargo.rustdocflag("--enable-index-page");
652+
cargo.rustdocflag("--show-type-layout");
651653
cargo.rustdocflag("-Zunstable-options");
652654
builder.run(&mut cargo.into());
653655
}

src/librustdoc/config.rs

+4
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ crate struct RenderOptions {
267267
crate document_hidden: bool,
268268
/// If `true`, generate a JSON file in the crate folder instead of HTML redirection files.
269269
crate generate_redirect_map: bool,
270+
/// Show the memory layout of types in the docs.
271+
crate show_type_layout: bool,
270272
crate unstable_features: rustc_feature::UnstableFeatures,
271273
crate emit: Vec<EmitType>,
272274
}
@@ -636,6 +638,7 @@ impl Options {
636638
let document_hidden = matches.opt_present("document-hidden-items");
637639
let run_check = matches.opt_present("check");
638640
let generate_redirect_map = matches.opt_present("generate-redirect-map");
641+
let show_type_layout = matches.opt_present("show-type-layout");
639642

640643
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
641644

@@ -695,6 +698,7 @@ impl Options {
695698
document_private,
696699
document_hidden,
697700
generate_redirect_map,
701+
show_type_layout,
698702
unstable_features: rustc_feature::UnstableFeatures::from_environment(
699703
crate_name.as_deref(),
700704
),

src/librustdoc/html/render/context.rs

+4
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ crate struct SharedContext<'tcx> {
9191
crate include_sources: bool,
9292
/// The local file sources we've emitted and their respective url-paths.
9393
crate local_sources: FxHashMap<PathBuf, String>,
94+
/// Show the memory layout of types in the docs.
95+
pub(super) show_type_layout: bool,
9496
/// Whether the collapsed pass ran
9597
collapsed: bool,
9698
/// The base-URL of the issue tracker for when an item has been tagged with
@@ -373,6 +375,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
373375
generate_search_filter,
374376
unstable_features,
375377
generate_redirect_map,
378+
show_type_layout,
376379
..
377380
} = options;
378381

@@ -446,6 +449,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
446449
all: RefCell::new(AllTypes::new()),
447450
errors: receiver,
448451
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
452+
show_type_layout,
449453
};
450454

451455
// Add the default themes to the `Vec` of stylepaths

src/librustdoc/html/render/print_item.rs

+74-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_hir as hir;
77
use rustc_hir::def::CtorKind;
88
use rustc_hir::def_id::DefId;
99
use rustc_middle::middle::stability;
10+
use rustc_middle::ty::layout::LayoutError;
1011
use rustc_middle::ty::TyCtxt;
1112
use rustc_span::hygiene::MacroKind;
1213
use rustc_span::symbol::{kw, sym, Symbol};
@@ -830,11 +831,12 @@ fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::T
830831

831832
document(w, cx, it, None);
832833

834+
let def_id = it.def_id.expect_real();
833835
// Render any items associated directly to this alias, as otherwise they
834836
// won't be visible anywhere in the docs. It would be nice to also show
835837
// associated items from the aliased type (see discussion in #32077), but
836838
// we need #14072 to make sense of the generics.
837-
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
839+
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
838840
}
839841

840842
fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
@@ -846,6 +848,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
846848
});
847849

848850
document(w, cx, it, None);
851+
849852
let mut fields = s
850853
.fields
851854
.iter()
@@ -880,7 +883,9 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
880883
document(w, cx, field, Some(it));
881884
}
882885
}
883-
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
886+
let def_id = it.def_id.expect_real();
887+
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
888+
document_type_layout(w, cx, def_id);
884889
}
885890

886891
fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
@@ -940,6 +945,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
940945
});
941946

942947
document(w, cx, it, None);
948+
943949
if !e.variants.is_empty() {
944950
write!(
945951
w,
@@ -1014,7 +1020,9 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
10141020
render_stability_since(w, variant, it, cx.tcx());
10151021
}
10161022
}
1017-
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
1023+
let def_id = it.def_id.expect_real();
1024+
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
1025+
document_type_layout(w, cx, def_id);
10181026
}
10191027

10201028
fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
@@ -1114,6 +1122,7 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St
11141122
});
11151123

11161124
document(w, cx, it, None);
1125+
11171126
let mut fields = s
11181127
.fields
11191128
.iter()
@@ -1152,7 +1161,9 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St
11521161
}
11531162
}
11541163
}
1155-
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
1164+
let def_id = it.def_id.expect_real();
1165+
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
1166+
document_type_layout(w, cx, def_id);
11561167
}
11571168

11581169
fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
@@ -1522,3 +1533,62 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
15221533
w.write_str("</div></details>");
15231534
}
15241535
}
1536+
1537+
fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
1538+
if !cx.shared.show_type_layout {
1539+
return;
1540+
}
1541+
1542+
writeln!(w, "<h2 class=\"small-section-header\">Layout</h2>");
1543+
writeln!(w, "<div class=\"docblock\">");
1544+
1545+
let tcx = cx.tcx();
1546+
let param_env = tcx.param_env(ty_def_id);
1547+
let ty = tcx.type_of(ty_def_id);
1548+
match tcx.layout_of(param_env.and(ty)) {
1549+
Ok(ty_layout) => {
1550+
writeln!(
1551+
w,
1552+
"<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
1553+
completely unstable and may be different between compiler versions and platforms. \
1554+
The only exception is types with certain <code>repr(...)</code> attributes. \
1555+
Please see the Rust Reference’s \
1556+
<a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
1557+
chapter for details on type layout guarantees.</p></div>"
1558+
);
1559+
if ty_layout.layout.abi.is_unsized() {
1560+
writeln!(w, "<p><strong>Size:</strong> (unsized)</p>");
1561+
} else {
1562+
let bytes = ty_layout.layout.size.bytes();
1563+
writeln!(
1564+
w,
1565+
"<p><strong>Size:</strong> {size} byte{pl}</p>",
1566+
size = bytes,
1567+
pl = if bytes == 1 { "" } else { "s" },
1568+
);
1569+
}
1570+
}
1571+
// This kind of layout error can occur with valid code, e.g. if you try to
1572+
// get the layout of a generic type such as `Vec<T>`.
1573+
Err(LayoutError::Unknown(_)) => {
1574+
writeln!(
1575+
w,
1576+
"<p><strong>Note:</strong> Unable to compute type layout, \
1577+
possibly due to this type having generic parameters. \
1578+
Layout can only be computed for concrete, fully-instantiated types.</p>"
1579+
);
1580+
}
1581+
// This kind of error probably can't happen with valid code, but we don't
1582+
// want to panic and prevent the docs from building, so we just let the
1583+
// user know that we couldn't compute the layout.
1584+
Err(LayoutError::SizeOverflow(_)) => {
1585+
writeln!(
1586+
w,
1587+
"<p><strong>Note:</strong> Encountered an error during type layout; \
1588+
the type was too big.</p>"
1589+
);
1590+
}
1591+
}
1592+
1593+
writeln!(w, "</div>");
1594+
}

src/librustdoc/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,9 @@ fn opts() -> Vec<RustcOptGroup> {
594594
)
595595
}),
596596
unstable("no-run", |o| o.optflag("", "no-run", "Compile doctests without running them")),
597+
unstable("show-type-layout", |o| {
598+
o.optflag("", "show-type-layout", "Include the memory layout of types in the docs")
599+
}),
597600
]
598601
}
599602

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Tests that `--show-type-layout` is required in order to show layout info.
2+
3+
// @!has type_layout_flag_required/struct.Foo.html 'Size: '
4+
pub struct Foo(usize);

src/test/rustdoc/type-layout.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// compile-flags: --show-type-layout -Z unstable-options
2+
3+
// @has type_layout/struct.Foo.html 'Size: '
4+
// @has - ' bytes'
5+
pub struct Foo {
6+
pub a: usize,
7+
b: Vec<String>,
8+
}
9+
10+
// @has type_layout/enum.Bar.html 'Size: '
11+
// @has - ' bytes'
12+
pub enum Bar<'a> {
13+
A(String),
14+
B(&'a str, (std::collections::HashMap<String, usize>, Foo)),
15+
}
16+
17+
// @has type_layout/union.Baz.html 'Size: '
18+
// @has - ' bytes'
19+
pub union Baz {
20+
a: &'static str,
21+
b: usize,
22+
c: &'static [u8],
23+
}
24+
25+
// @has type_layout/struct.X.html 'Size: '
26+
// @has - ' bytes'
27+
pub struct X(usize);
28+
29+
// @has type_layout/struct.Y.html 'Size: '
30+
// @has - '1 byte'
31+
// @!has - ' bytes'
32+
pub struct Y(u8);
33+
34+
// @has type_layout/struct.Z.html 'Size: '
35+
// @has - '0 bytes'
36+
pub struct Z;
37+
38+
// We can't compute layout for generic types.
39+
// @has type_layout/struct.Generic.html 'Unable to compute type layout, possibly due to this type having generic parameters'
40+
// @!has - 'Size: '
41+
pub struct Generic<T>(T);
42+
43+
// We *can*, however, compute layout for types that are only generic over lifetimes,
44+
// because lifetimes are a type-system construct.
45+
// @has type_layout/struct.GenericLifetimes.html 'Size: '
46+
// @has - ' bytes'
47+
pub struct GenericLifetimes<'a>(&'a str);
48+
49+
// @has type_layout/struct.Unsized.html 'Size: '
50+
// @has - '(unsized)'
51+
pub struct Unsized([u8]);
52+
53+
// @!has type_layout/trait.MyTrait.html 'Size: '
54+
pub trait MyTrait {}

0 commit comments

Comments
 (0)