rustdoc: migrate document_type_layout to askama

This commit is contained in:
Michael Howell 2023-04-04 14:15:29 -07:00
parent d1be642ce3
commit 08f204e17f
2 changed files with 112 additions and 99 deletions

View file

@ -5,13 +5,15 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId;
use rustc_index::vec::IndexVec;
use rustc_middle::middle::stability;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
use rustc_middle::ty::{self, Adt, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::abi::{LayoutS, Primitive, TagEncoding, Variants};
use rustc_target::abi::{LayoutS, Primitive, TagEncoding, VariantIdx, Variants};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
use std::rc::Rc;
@ -1936,111 +1938,77 @@ fn document_type_layout<'a, 'cx: 'a>(
cx: &'a Context<'cx>,
ty_def_id: DefId,
) -> impl fmt::Display + 'a + Captures<'cx> {
fn write_size_of_layout(mut w: impl fmt::Write, layout: &LayoutS, tag_size: u64) {
if layout.abi.is_unsized() {
write!(w, "(unsized)").unwrap();
} else {
let size = layout.size.bytes() - tag_size;
write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" }).unwrap();
if layout.abi.is_uninhabited() {
write!(
w,
" (<a href=\"https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited\">uninhabited</a>)"
).unwrap();
#[derive(Template)]
#[template(path = "type_layout.html")]
struct TypeLayout<'a, 'cx> {
cx: &'a Context<'cx>,
ty_def_id: DefId,
}
impl<'a, 'cx: 'a> TypeLayout<'a, 'cx> {
fn variants<'b: 'a>(&'b self) -> Option<&'b IndexVec<VariantIdx, LayoutS>> {
if let Variants::Multiple { variants, .. } =
self.type_layout().unwrap().layout.variants() && !variants.is_empty() {
Some(&variants)
} else {
None
}
}
fn type_layout<'b: 'a>(&'b self) -> Result<TyAndLayout<'cx>, LayoutError<'cx>> {
let tcx = self.cx.tcx();
let param_env = tcx.param_env(self.ty_def_id);
let ty = tcx.type_of(self.ty_def_id).subst_identity();
tcx.layout_of(param_env.and(ty))
}
fn variant_name<'b: 'a>(&'b self, index: VariantIdx) -> Symbol {
let Adt(adt, _) = self.type_layout().unwrap().ty.kind() else {
span_bug!(self.cx.tcx().def_span(self.ty_def_id), "not an adt")
};
adt.variant(index).name
}
fn tag_size<'b: 'a>(&'b self) -> u64 {
if let Variants::Multiple { variants, tag, tag_encoding, .. } =
self.type_layout().unwrap().layout.variants() && !variants.is_empty() {
if let TagEncoding::Niche { .. } = tag_encoding {
0
} else if let Primitive::Int(i, _) = tag.primitive() {
i.size().bytes()
} else {
span_bug!(self.cx.tcx().def_span(self.ty_def_id), "tag is neither niche nor int")
}
} else {
0
}
}
fn write_size<'b: 'a>(
&'b self,
layout: &'b LayoutS,
tag_size: u64,
) -> impl fmt::Display + Captures<'cx> + Captures<'b> {
display_fn(move |f| {
if layout.abi.is_unsized() {
write!(f, "(unsized)")?;
} else {
let size = layout.size.bytes() - tag_size;
write!(f, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" })?;
if layout.abi.is_uninhabited() {
write!(
f,
" (<a href=\"https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited\">uninhabited</a>)"
)?;
}
}
Ok(())
})
}
}
display_fn(move |mut f| {
display_fn(move |f| {
if !cx.shared.show_type_layout {
return Ok(());
}
writeln!(
f,
"<h2 id=\"layout\" class=\"small-section-header\"> \
Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
)?;
writeln!(f, "<div class=\"docblock\">")?;
let tcx = cx.tcx();
let param_env = tcx.param_env(ty_def_id);
let ty = tcx.type_of(ty_def_id).subst_identity();
match tcx.layout_of(param_env.and(ty)) {
Ok(ty_layout) => {
writeln!(
f,
"<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
<strong>completely unstable</strong> and may even differ between compilations. \
The only exception is types with certain <code>repr(...)</code> attributes. \
Please see the Rust References \
<a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
chapter for details on type layout guarantees.</p></div>"
)?;
f.write_str("<p><strong>Size:</strong> ")?;
write_size_of_layout(&mut f, &ty_layout.layout.0, 0);
writeln!(f, "</p>")?;
if let Variants::Multiple { variants, tag, tag_encoding, .. } =
&ty_layout.layout.variants()
{
if !variants.is_empty() {
f.write_str(
"<p><strong>Size for each variant:</strong></p>\
<ul>",
)?;
let Adt(adt, _) = ty_layout.ty.kind() else {
span_bug!(tcx.def_span(ty_def_id), "not an adt")
};
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")
};
for (index, layout) in variants.iter_enumerated() {
let name = adt.variant(index).name;
write!(&mut f, "<li><code>{name}</code>: ")?;
write_size_of_layout(&mut f, layout, tag_size);
writeln!(&mut f, "</li>")?;
}
f.write_str("</ul>")?;
}
}
}
// This kind of layout error can occur with valid code, e.g. if you try to
// get the layout of a generic type such as `Vec<T>`.
Err(LayoutError::Unknown(_)) => {
writeln!(
f,
"<p><strong>Note:</strong> Unable to compute type layout, \
possibly due to this type having generic parameters. \
Layout can only be computed for concrete, fully-instantiated types.</p>"
)?;
}
// This kind of error probably can't happen with valid code, but we don't
// want to panic and prevent the docs from building, so we just let the
// user know that we couldn't compute the layout.
Err(LayoutError::SizeOverflow(_)) => {
writeln!(
f,
"<p><strong>Note:</strong> Encountered an error during type layout; \
the type was too big.</p>"
)?;
}
Err(LayoutError::NormalizationFailure(_, _)) => {
writeln!(
f,
"<p><strong>Note:</strong> Encountered an error during type layout; \
the type failed to be normalized.</p>"
)?;
}
}
writeln!(f, "</div>")
Ok(TypeLayout { cx, ty_def_id }.render_into(f).unwrap())
})
}

View file

@ -0,0 +1,45 @@
<h2 id="layout" class="small-section-header"> {# #}
Layout<a href="#layout" class="anchor">§</a> {# #}
</h2> {# #}
<div class="docblock"> {# #}
{% match self.type_layout() %}
{% when Ok(ty_layout) %}
<div class="warning"> {# #}
<p> {# #}
<strong>Note:</strong> Most layout information is <strong>completely {#+ #}
unstable</strong> and may even differ between compilations. {#+ #}
The only exception is types with certain <code>repr(...)</code> {#+ #}
attributes. Please see the Rust References {#+ #}
<a href="https://doc.rust-lang.org/reference/type-layout.html">“Type Layout”</a> {#+ #}
chapter for details on type layout guarantees. {# #}
</p> {# #}
</div> {# #}
<p><strong>Size:</strong> {{ self.write_size(ty_layout.layout.0.borrow(), 0) | safe }}</p> {# #}
{% if let Some(variants) = self.variants() %}
<p><strong>Size for each variant:</strong></p> {# #}
<ul> {# #}
{% for (index, layout) in variants.iter_enumerated() %}
<li> {# #}
<code>{{ self.variant_name(index.clone()) }}</code>: {#+ #}
{{ self.write_size(layout, self.tag_size()) | safe }}
</li> {# #}
{% endfor %}
</ul> {# #}
{% endif %}
{# This kind of layout error can occur with valid code, e.g. if you try to
get the layout of a generic type such as `Vec<T>`. #}
{% when Err(LayoutError::Unknown(_)) %}
<p><strong>Note:</strong> Unable to compute type layout, {#+ #}
possibly due to this type having generic parameters. {#+ #}
Layout can only be computed for concrete, fully-instantiated types.</p> {# #}
{# This kind of error probably can't happen with valid code, but we don't
want to panic and prevent the docs from building, so we just let the
user know that we couldn't compute the layout. #}
{% when Err(LayoutError::SizeOverflow(_)) %}
<p><strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type was too big.</p> {# #}
{% when Err(LayoutError::NormalizationFailure(_, _)) %}
<p><strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type failed to be normalized.</p> {# #}
{% endmatch %}
</div> {# #}