From 1aebff96ade6518038e0f7522d3304bd0f9bf891 Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Tue, 6 Feb 2024 10:22:24 -0700 Subject: [PATCH 01/36] Add Top TOC support to rustdoc This commit adds the headers for the top level documentation to rustdoc's existing table of contents, along with associated items. It only show two levels of headers. Going further would require the sidebar to be wider, and that seems unnecessary (the crates that have manually-built TOCs usually don't need deeply nested headers). --- src/librustdoc/clean/types.rs | 6 - src/librustdoc/html/markdown.rs | 47 +++- src/librustdoc/html/render/context.rs | 3 +- src/librustdoc/html/render/sidebar.rs | 242 ++++++++++-------- src/librustdoc/html/static/css/rustdoc.css | 6 +- src/librustdoc/html/templates/sidebar.html | 17 +- src/librustdoc/html/toc.rs | 15 +- src/librustdoc/markdown.rs | 1 + tests/rustdoc-gui/sidebar.goml | 4 +- .../rustdoc/{ => sidebar}/sidebar-all-page.rs | 0 tests/rustdoc/{ => sidebar}/sidebar-items.rs | 0 .../{ => sidebar}/sidebar-link-generation.rs | 0 .../sidebar-links-to-foreign-impl.rs | 0 tests/rustdoc/sidebar/top-toc-html.rs | 19 ++ tests/rustdoc/sidebar/top-toc-idmap.rs | 44 ++++ 15 files changed, 271 insertions(+), 133 deletions(-) rename tests/rustdoc/{ => sidebar}/sidebar-all-page.rs (100%) rename tests/rustdoc/{ => sidebar}/sidebar-items.rs (100%) rename tests/rustdoc/{ => sidebar}/sidebar-link-generation.rs (100%) rename tests/rustdoc/{ => sidebar}/sidebar-links-to-foreign-impl.rs (100%) create mode 100644 tests/rustdoc/sidebar/top-toc-html.rs create mode 100644 tests/rustdoc/sidebar/top-toc-idmap.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ff5c16f2b3e..3ba4ae4bd0c 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -506,9 +506,6 @@ impl Item { pub(crate) fn is_mod(&self) -> bool { self.type_() == ItemType::Module } - pub(crate) fn is_trait(&self) -> bool { - self.type_() == ItemType::Trait - } pub(crate) fn is_struct(&self) -> bool { self.type_() == ItemType::Struct } @@ -536,9 +533,6 @@ impl Item { pub(crate) fn is_ty_method(&self) -> bool { self.type_() == ItemType::TyMethod } - pub(crate) fn is_type_alias(&self) -> bool { - self.type_() == ItemType::TypeAlias - } pub(crate) fn is_primitive(&self) -> bool { self.type_() == ItemType::Primitive } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 7bfe5d87d39..58795508a58 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -55,7 +55,7 @@ use crate::html::format::Buffer; use crate::html::highlight; use crate::html::length_limit::HtmlWithLimit; use crate::html::render::small_url_encode; -use crate::html::toc::TocBuilder; +use crate::html::toc::{Toc, TocBuilder}; #[cfg(test)] mod tests; @@ -101,6 +101,7 @@ pub struct Markdown<'a> { /// A struct like `Markdown` that renders the markdown with a table of contents. pub(crate) struct MarkdownWithToc<'a> { pub(crate) content: &'a str, + pub(crate) links: &'a [RenderedLink], pub(crate) ids: &'a mut IdMap, pub(crate) error_codes: ErrorCodes, pub(crate) edition: Edition, @@ -532,9 +533,9 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator let id = self.id_map.derive(id); if let Some(ref mut builder) = self.toc { - let mut html_header = String::new(); - html::push_html(&mut html_header, self.buf.iter().map(|(ev, _)| ev.clone())); - let sec = builder.push(level as u32, html_header, id.clone()); + let mut text_header = String::new(); + plain_text_from_events(self.buf.iter().map(|(ev, _)| ev.clone()), &mut text_header); + let sec = builder.push(level as u32, text_header, id.clone()); self.buf.push_front((Event::Html(format!("{sec} ").into()), 0..0)); } @@ -1415,10 +1416,23 @@ impl Markdown<'_> { } impl MarkdownWithToc<'_> { - pub(crate) fn into_string(self) -> String { - let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self; + pub(crate) fn into_parts(self) -> (Toc, String) { + let MarkdownWithToc { content: md, links, ids, error_codes: codes, edition, playground } = + self; - let p = Parser::new_ext(md, main_body_opts()).into_offset_iter(); + // This is actually common enough to special-case + if md.is_empty() { + return (Toc { entries: Vec::new() }, String::new()); + } + let mut replacer = |broken_link: BrokenLink<'_>| { + links + .iter() + .find(|link| &*link.original_text == &*broken_link.reference) + .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) + }; + + let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut replacer)); + let p = p.into_offset_iter(); let mut s = String::with_capacity(md.len() * 3 / 2); @@ -1432,7 +1446,11 @@ impl MarkdownWithToc<'_> { html::push_html(&mut s, p); } - format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.into_toc().print()) + (toc.into_toc(), s) + } + pub(crate) fn into_string(self) -> String { + let (toc, s) = self.into_parts(); + format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.print()) } } @@ -1611,7 +1629,16 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)); - for event in p { + plain_text_from_events(p, &mut s); + + s +} + +pub(crate) fn plain_text_from_events<'a>( + events: impl Iterator<Item = pulldown_cmark::Event<'a>>, + s: &mut String, +) { + for event in events { match &event { Event::Text(text) => s.push_str(text), Event::Code(code) => { @@ -1626,8 +1653,6 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin _ => (), } } - - s } #[derive(Debug)] diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 0ed8921b1e8..5734b59134a 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -616,7 +616,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { let all = shared.all.replace(AllTypes::new()); let mut sidebar = Buffer::html(); - let blocks = sidebar_module_like(all.item_sections()); + // all.html is not customizable, so a blank id map is fine + let blocks = sidebar_module_like(all.item_sections(), &mut IdMap::new()); let bar = Sidebar { title_prefix: "", title: "", diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 101cc839f09..a5a22618f22 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -7,12 +7,13 @@ use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefIdSet; use rustc_middle::ty::{self, TyCtxt}; +use crate::{ + clean, + formats::{item_type::ItemType, Impl}, + html::{format::Buffer, markdown::IdMap, markdown::MarkdownWithToc}, +}; + use super::{item_ty_to_section, Context, ItemSection}; -use crate::clean; -use crate::formats::item_type::ItemType; -use crate::formats::Impl; -use crate::html::format::Buffer; -use crate::html::markdown::IdMap; #[derive(Template)] #[template(path = "sidebar.html")] @@ -66,11 +67,13 @@ pub(crate) struct Link<'a> { name: Cow<'a, str>, /// The id of an anchor within the page (without a `#` prefix) href: Cow<'a, str>, + /// Nested list of links (used only in top-toc) + children: Vec<Link<'a>>, } impl<'a> Link<'a> { pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self { - Self { href: href.into(), name: name.into() } + Self { href: href.into(), name: name.into(), children: vec![] } } pub fn empty() -> Link<'static> { Link::new("", "") @@ -94,17 +97,19 @@ pub(crate) mod filters { } pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { - let blocks: Vec<LinkBlock<'_>> = match *it.kind { - clean::StructItem(ref s) => sidebar_struct(cx, it, s), - clean::TraitItem(ref t) => sidebar_trait(cx, it, t), - clean::PrimitiveItem(_) => sidebar_primitive(cx, it), - clean::UnionItem(ref u) => sidebar_union(cx, it, u), - clean::EnumItem(ref e) => sidebar_enum(cx, it, e), - clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t), - clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)], - clean::ForeignTypeItem => sidebar_foreign_type(cx, it), - _ => vec![], - }; + let mut ids = IdMap::new(); + let mut blocks: Vec<LinkBlock<'_>> = docblock_toc(cx, it, &mut ids).into_iter().collect(); + match *it.kind { + clean::StructItem(ref s) => sidebar_struct(cx, it, s, &mut blocks), + clean::TraitItem(ref t) => sidebar_trait(cx, it, t, &mut blocks), + clean::PrimitiveItem(_) => sidebar_primitive(cx, it, &mut blocks), + clean::UnionItem(ref u) => sidebar_union(cx, it, u, &mut blocks), + clean::EnumItem(ref e) => sidebar_enum(cx, it, e, &mut blocks), + clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t, &mut blocks), + clean::ModuleItem(ref m) => blocks.push(sidebar_module(&m.items, &mut ids)), + clean::ForeignTypeItem => sidebar_foreign_type(cx, it, &mut blocks), + _ => {} + } // The sidebar is designed to display sibling functions, modules and // other miscellaneous information. since there are lots of sibling // items (and that causes quadratic growth in large modules), @@ -112,15 +117,9 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf // still, we don't move everything into JS because we want to preserve // as much HTML as possible in order to allow non-JS-enabled browsers // to navigate the documentation (though slightly inefficiently). - let (title_prefix, title) = if it.is_struct() - || it.is_trait() - || it.is_primitive() - || it.is_union() - || it.is_enum() - // crate title is displayed as part of logo lockup - || (it.is_mod() && !it.is_crate()) - || it.is_type_alias() - { + // + // crate title is displayed as part of logo lockup + let (title_prefix, title) = if !blocks.is_empty() && !it.is_crate() { ( match *it.kind { clean::ModuleItem(..) => "Module ", @@ -162,30 +161,75 @@ fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> { fields } +fn docblock_toc<'a>( + cx: &'a Context<'_>, + it: &'a clean::Item, + ids: &mut IdMap, +) -> Option<LinkBlock<'a>> { + let (toc, _) = MarkdownWithToc { + content: &it.doc_value(), + links: &it.links(cx), + ids, + error_codes: cx.shared.codes, + edition: cx.shared.edition(), + playground: &cx.shared.playground, + custom_code_classes_in_docs: cx.tcx().features().custom_code_classes_in_docs, + } + .into_parts(); + let links: Vec<Link<'_>> = toc + .entries + .into_iter() + .map(|entry| { + Link { + name: entry.name.into(), + href: entry.id.into(), + children: entry + .children + .entries + .into_iter() + .map(|entry| Link { + name: entry.name.into(), + href: entry.id.into(), + // Only a single level of nesting is shown here. + // Going the full six could break the layout, + // so we have to cut it off somewhere. + children: vec![], + }) + .collect(), + } + }) + .collect(); + if links.is_empty() { + None + } else { + Some(LinkBlock::new(Link::new("#", "Sections"), "top-toc", links)) + } +} + fn sidebar_struct<'a>( cx: &'a Context<'_>, it: &'a clean::Item, s: &'a clean::Struct, -) -> Vec<LinkBlock<'a>> { + items: &mut Vec<LinkBlock<'a>>, +) { let fields = get_struct_fields_name(&s.fields); let field_name = match s.ctor_kind { Some(CtorKind::Fn) => Some("Tuple Fields"), None => Some("Fields"), _ => None, }; - let mut items = vec![]; if let Some(name) = field_name { items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields)); } - sidebar_assoc_items(cx, it, &mut items); - items + sidebar_assoc_items(cx, it, items); } fn sidebar_trait<'a>( cx: &'a Context<'_>, it: &'a clean::Item, t: &'a clean::Trait, -) -> Vec<LinkBlock<'a>> { + blocks: &mut Vec<LinkBlock<'a>>, +) { fn filter_items<'a>( items: &'a [clean::Item], filt: impl Fn(&clean::Item) -> bool, @@ -222,19 +266,20 @@ fn sidebar_trait<'a>( foreign_impls.sort(); } - let mut blocks: Vec<LinkBlock<'_>> = [ - ("required-associated-types", "Required Associated Types", req_assoc), - ("provided-associated-types", "Provided Associated Types", prov_assoc), - ("required-associated-consts", "Required Associated Constants", req_assoc_const), - ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const), - ("required-methods", "Required Methods", req_method), - ("provided-methods", "Provided Methods", prov_method), - ("foreign-impls", "Implementations on Foreign Types", foreign_impls), - ] - .into_iter() - .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)) - .collect(); - sidebar_assoc_items(cx, it, &mut blocks); + blocks.extend( + [ + ("required-associated-types", "Required Associated Types", req_assoc), + ("provided-associated-types", "Provided Associated Types", prov_assoc), + ("required-associated-consts", "Required Associated Constants", req_assoc_const), + ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const), + ("required-methods", "Required Methods", req_method), + ("provided-methods", "Provided Methods", prov_method), + ("foreign-impls", "Implementations on Foreign Types", foreign_impls), + ] + .into_iter() + .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)), + ); + sidebar_assoc_items(cx, it, blocks); if !t.is_object_safe(cx.tcx()) { blocks.push(LinkBlock::forced( @@ -250,20 +295,17 @@ fn sidebar_trait<'a>( "impl-auto", )); } - blocks } -fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> { +fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item, items: &mut Vec<LinkBlock<'a>>) { if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { - let mut items = vec![]; - sidebar_assoc_items(cx, it, &mut items); - items + sidebar_assoc_items(cx, it, items); } else { let shared = Rc::clone(&cx.shared); let (concrete, synthetic, blanket_impl) = super::get_filtered_impls_for_reference(&shared, it); - sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl).into() + sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl, items); } } @@ -271,8 +313,8 @@ fn sidebar_type_alias<'a>( cx: &'a Context<'_>, it: &'a clean::Item, t: &'a clean::TypeAlias, -) -> Vec<LinkBlock<'a>> { - let mut items = vec![]; + items: &mut Vec<LinkBlock<'a>>, +) { if let Some(inner_type) = &t.inner_type { items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type")); match inner_type { @@ -294,19 +336,18 @@ fn sidebar_type_alias<'a>( } } } - sidebar_assoc_items(cx, it, &mut items); - items + sidebar_assoc_items(cx, it, items); } fn sidebar_union<'a>( cx: &'a Context<'_>, it: &'a clean::Item, u: &'a clean::Union, -) -> Vec<LinkBlock<'a>> { + items: &mut Vec<LinkBlock<'a>>, +) { let fields = get_struct_fields_name(&u.fields); - let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)]; - sidebar_assoc_items(cx, it, &mut items); - items + items.push(LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)); + sidebar_assoc_items(cx, it, items); } /// Adds trait implementations into the blocks of links @@ -345,33 +386,6 @@ fn sidebar_assoc_items<'a>( methods.sort(); } - let mut deref_methods = Vec::new(); - let [concrete, synthetic, blanket] = if v.iter().any(|i| i.inner_impl().trait_.is_some()) { - if let Some(impl_) = - v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) - { - let mut derefs = DefIdSet::default(); - derefs.insert(did); - sidebar_deref_methods( - cx, - &mut deref_methods, - impl_, - v, - &mut derefs, - &mut used_links, - ); - } - - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = - v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = - concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket()); - - sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl) - } else { - std::array::from_fn(|_| LinkBlock::new(Link::empty(), "", vec![])) - }; - let mut blocks = vec![ LinkBlock::new( Link::new("implementations", "Associated Constants"), @@ -380,8 +394,30 @@ fn sidebar_assoc_items<'a>( ), LinkBlock::new(Link::new("implementations", "Methods"), "method", methods), ]; - blocks.append(&mut deref_methods); - blocks.extend([concrete, synthetic, blanket]); + + if v.iter().any(|i| i.inner_impl().trait_.is_some()) { + if let Some(impl_) = + v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) + { + let mut derefs = DefIdSet::default(); + derefs.insert(did); + sidebar_deref_methods(cx, &mut blocks, impl_, v, &mut derefs, &mut used_links); + } + + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = + v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto()); + let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = + concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket()); + + sidebar_render_assoc_items( + cx, + &mut id_map, + concrete, + synthetic, + blanket_impl, + &mut blocks, + ); + } links.append(&mut blocks); } } @@ -471,7 +507,8 @@ fn sidebar_enum<'a>( cx: &'a Context<'_>, it: &'a clean::Item, e: &'a clean::Enum, -) -> Vec<LinkBlock<'a>> { + items: &mut Vec<LinkBlock<'a>>, +) { let mut variants = e .variants() .filter_map(|v| v.name) @@ -479,24 +516,24 @@ fn sidebar_enum<'a>( .collect::<Vec<_>>(); variants.sort_unstable(); - let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)]; - sidebar_assoc_items(cx, it, &mut items); - items + items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)); + sidebar_assoc_items(cx, it, items); } pub(crate) fn sidebar_module_like( item_sections_in_use: FxHashSet<ItemSection>, + ids: &mut IdMap, ) -> LinkBlock<'static> { let item_sections = ItemSection::ALL .iter() .copied() .filter(|sec| item_sections_in_use.contains(sec)) - .map(|sec| Link::new(sec.id(), sec.name())) + .map(|sec| Link::new(ids.derive(sec.id()), sec.name())) .collect(); LinkBlock::new(Link::empty(), "", item_sections) } -fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> { +fn sidebar_module(items: &[clean::Item], ids: &mut IdMap) -> LinkBlock<'static> { let item_sections_in_use: FxHashSet<_> = items .iter() .filter(|it| { @@ -517,13 +554,15 @@ fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> { .map(|it| item_ty_to_section(it.type_())) .collect(); - sidebar_module_like(item_sections_in_use) + sidebar_module_like(item_sections_in_use, ids) } -fn sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> { - let mut items = vec![]; - sidebar_assoc_items(cx, it, &mut items); - items +fn sidebar_foreign_type<'a>( + cx: &'a Context<'_>, + it: &'a clean::Item, + items: &mut Vec<LinkBlock<'a>>, +) { + sidebar_assoc_items(cx, it, items); } /// Renders the trait implementations for this type @@ -533,7 +572,8 @@ fn sidebar_render_assoc_items( concrete: Vec<&Impl>, synthetic: Vec<&Impl>, blanket_impl: Vec<&Impl>, -) -> [LinkBlock<'static>; 3] { + items: &mut Vec<LinkBlock<'_>>, +) { let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { let mut links = FxHashSet::default(); @@ -558,7 +598,7 @@ fn sidebar_render_assoc_items( let concrete = format_impls(concrete, id_map); let synthetic = format_impls(synthetic, id_map); let blanket = format_impls(blanket_impl, id_map); - [ + items.extend([ LinkBlock::new( Link::new("trait-implementations", "Trait Implementations"), "trait-implementation", @@ -574,7 +614,7 @@ fn sidebar_render_assoc_items( "blanket-implementation", blanket, ), - ] + ]); } fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String { diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index eb5a5d935e2..58e27735035 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -568,12 +568,16 @@ img { width: 48px; } -ul.block, .block li { +ul.block, .block li, .block ul { padding: 0; margin: 0; list-style: none; } +.block ul { + margin-left: 12px; +} + .sidebar-elems a, .sidebar > h2 a { display: block; diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index 0990c2716b8..ac6aa849f5c 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -15,14 +15,23 @@ {% for block in blocks %} {% if block.should_render() %} {% if !block.heading.name.is_empty() %} - <h3><a href="#{{block.heading.href|safe}}"> {# #} - {{block.heading.name|wrapped|safe}} {# #} - </a></h3> {# #} + <h3{% if !block.class.is_empty() +%} class="{{block.class}}"{% endif %}> {# #} + <a href="#{{block.heading.href|safe}}">{{block.heading.name|wrapped|safe}}</a> {# #} + </h3> {# #} {% endif %} {% if !block.links.is_empty() %} <ul class="block{% if !block.class.is_empty() +%} {{+block.class}}{% endif %}"> {% for link in block.links %} - <li><a href="#{{link.href|safe}}">{{link.name}}</a></li> + <li> {# #} + <a href="#{{link.href|safe}}">{{link.name}}</a> {# #} + {% if !link.children.is_empty() %} + <ul> {# #} + {% for child in link.children %} + <li><a href="#{{child.href|safe}}">{{child.name}}</a></li> + {% endfor %} + </ul> {# #} + {% endif %} + </li> {# #} {% endfor %} </ul> {% endif %} diff --git a/src/librustdoc/html/toc.rs b/src/librustdoc/html/toc.rs index a12c2a6a16c..a44c4a1b59e 100644 --- a/src/librustdoc/html/toc.rs +++ b/src/librustdoc/html/toc.rs @@ -1,4 +1,5 @@ //! Table-of-contents creation. +use crate::html::escape::EscapeBodyText; /// A (recursive) table of contents #[derive(Debug, PartialEq)] @@ -16,7 +17,7 @@ pub(crate) struct Toc { /// ### A /// ## B /// ``` - entries: Vec<TocEntry>, + pub(crate) entries: Vec<TocEntry>, } impl Toc { @@ -27,11 +28,11 @@ impl Toc { #[derive(Debug, PartialEq)] pub(crate) struct TocEntry { - level: u32, - sec_number: String, - name: String, - id: String, - children: Toc, + pub(crate) level: u32, + pub(crate) sec_number: String, + pub(crate) name: String, + pub(crate) id: String, + pub(crate) children: Toc, } /// Progressive construction of a table of contents. @@ -173,7 +174,7 @@ impl Toc { "\n<li><a href=\"#{id}\">{num} {name}</a>", id = entry.id, num = entry.sec_number, - name = entry.name + name = EscapeBodyText(&entry.name) ); entry.children.print_inner(&mut *v); v.push_str("</li>"); diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index a98f81d011e..581ebbbe58d 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -72,6 +72,7 @@ pub(crate) fn render<P: AsRef<Path>>( let text = if !options.markdown_no_toc { MarkdownWithToc { content: text, + links: &[], ids: &mut ids, error_codes, edition, diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml index e499c159c6c..5ac36bd4cc2 100644 --- a/tests/rustdoc-gui/sidebar.goml +++ b/tests/rustdoc-gui/sidebar.goml @@ -118,7 +118,7 @@ assert-false: ".sidebar-elems > .crate" go-to: "./module/index.html" assert-property: (".sidebar", {"clientWidth": "200"}) assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2") -assert-text: (".sidebar > .location", "Module module") +assert-text: (".sidebar .location", "Module module") assert-count: (".sidebar .location", 1) assert-text: (".sidebar-elems ul.block > li.current > a", "module") // Module page requires three headings: @@ -136,7 +136,7 @@ assert-false: ".sidebar-elems > .crate" go-to: "./sub_module/sub_sub_module/index.html" assert-property: (".sidebar", {"clientWidth": "200"}) assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2") -assert-text: (".sidebar > .location", "Module sub_sub_module") +assert-text: (".sidebar .location", "Module sub_sub_module") assert-text: (".sidebar > .sidebar-elems > h2", "In lib2::module::sub_module") assert-property: (".sidebar > .sidebar-elems > h2 > a", { "href": "/module/sub_module/index.html", diff --git a/tests/rustdoc/sidebar-all-page.rs b/tests/rustdoc/sidebar/sidebar-all-page.rs similarity index 100% rename from tests/rustdoc/sidebar-all-page.rs rename to tests/rustdoc/sidebar/sidebar-all-page.rs diff --git a/tests/rustdoc/sidebar-items.rs b/tests/rustdoc/sidebar/sidebar-items.rs similarity index 100% rename from tests/rustdoc/sidebar-items.rs rename to tests/rustdoc/sidebar/sidebar-items.rs diff --git a/tests/rustdoc/sidebar-link-generation.rs b/tests/rustdoc/sidebar/sidebar-link-generation.rs similarity index 100% rename from tests/rustdoc/sidebar-link-generation.rs rename to tests/rustdoc/sidebar/sidebar-link-generation.rs diff --git a/tests/rustdoc/sidebar-links-to-foreign-impl.rs b/tests/rustdoc/sidebar/sidebar-links-to-foreign-impl.rs similarity index 100% rename from tests/rustdoc/sidebar-links-to-foreign-impl.rs rename to tests/rustdoc/sidebar/sidebar-links-to-foreign-impl.rs diff --git a/tests/rustdoc/sidebar/top-toc-html.rs b/tests/rustdoc/sidebar/top-toc-html.rs new file mode 100644 index 00000000000..6fc84c1964c --- /dev/null +++ b/tests/rustdoc/sidebar/top-toc-html.rs @@ -0,0 +1,19 @@ +// ignore-tidy-linelength + +#![crate_name = "foo"] +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +//! # Basic [link](https://example.com) and *emphasis* +//! +//! This test case covers TOC entries with rich text inside. +//! Rustdoc normally supports headers with links, but for the +//! TOC, that would break the layout. +//! +//! For consistency, emphasis is also filtered out. + +// @has foo/index.html +// User header +// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]' 'Basic link and emphasis' +// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]/em' 0 +// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]/a' 0 diff --git a/tests/rustdoc/sidebar/top-toc-idmap.rs b/tests/rustdoc/sidebar/top-toc-idmap.rs new file mode 100644 index 00000000000..ccfc7336e9f --- /dev/null +++ b/tests/rustdoc/sidebar/top-toc-idmap.rs @@ -0,0 +1,44 @@ +#![crate_name = "foo"] +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +//! # Structs +//! +//! This header has the same name as a built-in header, +//! and we need to make sure they're disambiguated with +//! suffixes. +//! +//! Module-like headers get derived from the internal ID map, +//! so the *internal* one gets a suffix here. To make sure it +//! works right, the one in the `top-toc` needs to match the one +//! in the `top-doc`, and the one that's not in the `top-doc` +//! needs to match the one that isn't in the `top-toc`. + +// @has foo/index.html +// User header +// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#structs"]' 'Structs' +// @has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="structs"]' 'Structs' +// Built-in header +// @has - '//section[@id="TOC"]/ul[@class="block"]/li/a[@href="#structs-1"]' 'Structs' +// @has - '//section[@id="main-content"]/h2[@id="structs-1"]' 'Structs' + +/// # Fields +/// ## Fields +/// ### Fields +/// +/// The difference between struct-like headers and module-like headers +/// is strange, but not actually a problem as long as we're consistent. + +// @has foo/struct.MyStruct.html +// User header +// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#fields-1"]' 'Fields' +// @has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="fields-1"]' 'Fields' +// Only one level of nesting +// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]//a' 2 +// Built-in header +// @has - '//section[@id="TOC"]/h3/a[@href="#fields"]' 'Fields' +// @has - '//section[@id="main-content"]/h2[@id="fields"]' 'Fields' + +pub struct MyStruct { + pub fields: i32, +} From a7aea5d96bcafb7046ed7440122395e7f5e5d43d Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Tue, 6 Feb 2024 18:21:48 -0700 Subject: [PATCH 02/36] Add configuration options to hide TOC or module navigation --- src/librustdoc/html/render/context.rs | 1 + src/librustdoc/html/render/sidebar.rs | 15 +++++--- src/librustdoc/html/static/css/rustdoc.css | 12 +++++-- src/librustdoc/html/static/js/main.js | 4 +-- src/librustdoc/html/static/js/settings.js | 29 +++++++++++++++ src/librustdoc/html/static/js/storage.js | 13 ++++--- src/librustdoc/html/templates/sidebar.html | 20 ++++++----- tests/rustdoc-gui/sidebar.goml | 41 +++++++++++++++++++--- 8 files changed, 111 insertions(+), 24 deletions(-) diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 5734b59134a..a167681b316 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -623,6 +623,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { title: "", is_crate: false, is_mod: false, + parent_is_crate: false, blocks: vec![blocks], path: String::new(), }; diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index a5a22618f22..5e59754da79 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -21,6 +21,7 @@ pub(super) struct Sidebar<'a> { pub(super) title_prefix: &'static str, pub(super) title: &'a str, pub(super) is_crate: bool, + pub(super) parent_is_crate: bool, pub(super) is_mod: bool, pub(super) blocks: Vec<LinkBlock<'a>>, pub(super) path: String, @@ -144,8 +145,15 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf } else { "".into() }; - let sidebar = - Sidebar { title_prefix, title, is_mod: it.is_mod(), is_crate: it.is_crate(), blocks, path }; + let sidebar = Sidebar { + title_prefix, + title, + is_mod: it.is_mod(), + is_crate: it.is_crate(), + parent_is_crate: sidebar_path.len() == 1, + blocks, + path, + }; sidebar.render_into(buffer).unwrap(); } @@ -173,7 +181,6 @@ fn docblock_toc<'a>( error_codes: cx.shared.codes, edition: cx.shared.edition(), playground: &cx.shared.playground, - custom_code_classes_in_docs: cx.tcx().features().custom_code_classes_in_docs, } .into_parts(); let links: Vec<Link<'_>> = toc @@ -202,7 +209,7 @@ fn docblock_toc<'a>( if links.is_empty() { None } else { - Some(LinkBlock::new(Link::new("#", "Sections"), "top-toc", links)) + Some(LinkBlock::new(Link::new("", "Sections"), "top-toc", links)) } } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 58e27735035..c2bb5fa8f2a 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -574,8 +574,8 @@ ul.block, .block li, .block ul { list-style: none; } -.block ul { - margin-left: 12px; +.block ul a { + padding-left: 1rem; } .sidebar-elems a, @@ -589,6 +589,14 @@ ul.block, .block li, .block ul { background-clip: border-box; } +.hide-toc #TOC, .hide-toc .in-crate { + display: none; +} + +.hide-modnav #ModNav { + display: none; +} + .sidebar h2 { text-wrap: balance; overflow-wrap: anywhere; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 75f2a1418cd..4135341b22e 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -499,7 +499,7 @@ function preLoadCss(cssUrl) { if (!window.SIDEBAR_ITEMS) { return; } - const sidebar = document.getElementsByClassName("sidebar-elems")[0]; + const sidebar = document.getElementById("ModNav"); /** * Append to the sidebar a "block" of links - a heading along with a list (`<ul>`) of items. @@ -885,7 +885,7 @@ function preLoadCss(cssUrl) { if (!window.ALL_CRATES) { return; } - const sidebarElems = document.getElementsByClassName("sidebar-elems")[0]; + const sidebarElems = document.getElementById("ModNav"); if (!sidebarElems) { return; } diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 2b42fbebb80..c52a19ef987 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -36,6 +36,20 @@ removeClass(document.documentElement, "hide-sidebar"); } break; + case "hide-toc": + if (value === true) { + addClass(document.documentElement, "hide-toc"); + } else { + removeClass(document.documentElement, "hide-toc"); + } + break; + case "hide-modnav": + if (value === true) { + addClass(document.documentElement, "hide-modnav"); + } else { + removeClass(document.documentElement, "hide-modnav"); + } + break; } } @@ -102,6 +116,11 @@ let output = ""; for (const setting of settings) { + if (setting === "hr") { + output += "<hr>"; + continue; + } + const js_data_name = setting["js_name"]; const setting_name = setting["name"]; @@ -198,6 +217,16 @@ "js_name": "hide-sidebar", "default": false, }, + { + "name": "Hide table of contents", + "js_name": "hide-toc", + "default": false, + }, + { + "name": "Hide module navigation", + "js_name": "hide-modnav", + "default": false, + }, { "name": "Disable keyboard shortcuts", "js_name": "disable-shortcuts", diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 4a27ca92fff..d75fb7a7fb5 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -196,16 +196,21 @@ updateTheme(); // This needs to be done here because this JS is render-blocking, // so that the sidebar doesn't "jump" after appearing on screen. // The user interaction to change this is set up in main.js. +// +// At this point in page load, `document.body` is not available yet. +// Set a class on the `<html>` element instead. if (getSettingValue("source-sidebar-show") === "true") { - // At this point in page load, `document.body` is not available yet. - // Set a class on the `<html>` element instead. addClass(document.documentElement, "src-sidebar-expanded"); } if (getSettingValue("hide-sidebar") === "true") { - // At this point in page load, `document.body` is not available yet. - // Set a class on the `<html>` element instead. addClass(document.documentElement, "hide-sidebar"); } +if (getSettingValue("hide-toc") === "true") { + addClass(document.documentElement, "hide-toc"); +} +if (getSettingValue("hide-modnav") === "true") { + addClass(document.documentElement, "hide-modnav"); +} function updateSidebarWidth() { const desktopSidebarWidth = getSettingValue("desktop-sidebar-width"); if (desktopSidebarWidth && desktopSidebarWidth !== "null") { diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index ac6aa849f5c..c0a9b254425 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -1,8 +1,3 @@ -{% if !title.is_empty() %} - <h2 class="location"> {# #} - <a href="#">{{title_prefix}}{{title|wrapped|safe}}</a> {# #} - </h2> -{% endif %} <div class="sidebar-elems"> {% if is_crate %} <ul class="block"> {# #} @@ -11,11 +6,16 @@ {% endif %} {% if self.should_render_blocks() %} - <section> + <section id="TOC"> + {% if !title.is_empty() %} + <h2 class="location"> {# #} + <a href="#">{{title_prefix}}{{title|wrapped|safe}}</a> {# #} + </h2> + {% endif %} {% for block in blocks %} {% if block.should_render() %} {% if !block.heading.name.is_empty() %} - <h3{% if !block.class.is_empty() +%} class="{{block.class}}"{% endif %}> {# #} + <h3> {# #} <a href="#{{block.heading.href|safe}}">{{block.heading.name|wrapped|safe}}</a> {# #} </h3> {# #} {% endif %} @@ -39,7 +39,11 @@ {% endfor %} </section> {% endif %} + <div id="ModNav"> {% if !path.is_empty() %} - <h2><a href="{% if is_mod %}../{% endif %}index.html">In {{+ path|wrapped|safe}}</a></h2> + <h2{% if parent_is_crate +%} class="in-crate"{% endif %}> {# #} + <a href="{% if is_mod %}../{% endif %}index.html">In {{+ path|wrapped|safe}}</a> {# #} + </h2> {# #} {% endif %} + </div> {# #} </div> diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml index 5ac36bd4cc2..3253e6ea86a 100644 --- a/tests/rustdoc-gui/sidebar.goml +++ b/tests/rustdoc-gui/sidebar.goml @@ -126,8 +126,8 @@ assert-text: (".sidebar-elems ul.block > li.current > a", "module") // - Module name, followed by TOC for module headings // - "In crate [name]" parent pointer, followed by sibling navigation assert-count: (".sidebar h2", 3) -assert-text: (".sidebar > .sidebar-elems > h2", "In crate lib2") -assert-property: (".sidebar > .sidebar-elems > h2 > a", { +assert-text: (".sidebar > .sidebar-elems > #ModNav > h2", "In crate lib2") +assert-property: (".sidebar > .sidebar-elems > #ModNav > h2 > a", { "href": "/lib2/index.html", }, ENDS_WITH) // We check that we don't have the crate list. @@ -137,8 +137,8 @@ go-to: "./sub_module/sub_sub_module/index.html" assert-property: (".sidebar", {"clientWidth": "200"}) assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2") assert-text: (".sidebar .location", "Module sub_sub_module") -assert-text: (".sidebar > .sidebar-elems > h2", "In lib2::module::sub_module") -assert-property: (".sidebar > .sidebar-elems > h2 > a", { +assert-text: (".sidebar > .sidebar-elems > #ModNav > h2", "In lib2::module::sub_module") +assert-property: (".sidebar > .sidebar-elems > #ModNav > h2 > a", { "href": "/module/sub_module/index.html", }, ENDS_WITH) assert-text: (".sidebar-elems ul.block > li.current > a", "sub_sub_module") @@ -198,3 +198,36 @@ assert-position-false: (".sidebar-crate > h2 > a", {"x": -3}) // when line-wrapped, see that it becomes flush-left again drag-and-drop: ((205, 100), (108, 100)) assert-position: (".sidebar-crate > h2 > a", {"x": -3}) + +// Configuration option to show TOC in sidebar. +set-local-storage: {"rustdoc-hide-toc": "true"} +go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" +assert-css: ("#TOC", {"display": "none"}) +assert-css: (".sidebar .in-crate", {"display": "none"}) +set-local-storage: {"rustdoc-hide-toc": "false"} +go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" +assert-css: ("#TOC", {"display": "block"}) +assert-css: (".sidebar .in-crate", {"display": "block"}) + +set-local-storage: {"rustdoc-hide-modnav": "true"} +go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" +assert-css: ("#ModNav", {"display": "none"}) +set-local-storage: {"rustdoc-hide-modnav": "false"} +go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" +assert-css: ("#ModNav", {"display": "block"}) + +set-local-storage: {"rustdoc-hide-toc": "true"} +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" +assert-css: ("#TOC", {"display": "none"}) +assert-false: ".sidebar .in-crate" +set-local-storage: {"rustdoc-hide-toc": "false"} +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" +assert-css: ("#TOC", {"display": "block"}) +assert-false: ".sidebar .in-crate" + +set-local-storage: {"rustdoc-hide-modnav": "true"} +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" +assert-css: ("#ModNav", {"display": "none"}) +set-local-storage: {"rustdoc-hide-modnav": "false"} +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" +assert-css: ("#ModNav", {"display": "block"}) From 5a6054b4a2850d195bf54d7176b4a32382a8df49 Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Mon, 22 Jul 2024 10:31:24 -0700 Subject: [PATCH 03/36] rustdoc: add separate section for module items --- src/librustdoc/html/render/context.rs | 9 +++++--- src/librustdoc/html/render/sidebar.rs | 27 +++++++++++++++++++--- src/librustdoc/html/templates/sidebar.html | 10 ++++---- tests/rustdoc/sidebar/top-toc-nil.rs | 7 ++++++ 4 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 tests/rustdoc/sidebar/top-toc-nil.rs diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index a167681b316..510da99c9ef 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -14,9 +14,12 @@ use rustc_span::edition::Edition; use rustc_span::{sym, FileName, Symbol}; use super::print_item::{full_path, item_path, print_item}; -use super::sidebar::{print_sidebar, sidebar_module_like, Sidebar}; use super::write_shared::write_shared; -use super::{collect_spans_and_sources, scrape_examples_help, AllTypes, LinkFromSrc, StylePath}; +use super::{ + collect_spans_and_sources, scrape_examples_help, + sidebar::{print_sidebar, sidebar_module_like, ModuleLike, Sidebar}, + AllTypes, LinkFromSrc, StylePath, +}; use crate::clean::types::ExternalLocation; use crate::clean::utils::has_doc_flag; use crate::clean::{self, ExternalCrate}; @@ -617,7 +620,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { let mut sidebar = Buffer::html(); // all.html is not customizable, so a blank id map is fine - let blocks = sidebar_module_like(all.item_sections(), &mut IdMap::new()); + let blocks = sidebar_module_like(all.item_sections(), &mut IdMap::new(), ModuleLike::Crate); let bar = Sidebar { title_prefix: "", title: "", diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 5e59754da79..c76253c9fc3 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -15,6 +15,18 @@ use crate::{ use super::{item_ty_to_section, Context, ItemSection}; +#[derive(Clone, Copy)] +pub(crate) enum ModuleLike { + Module, + Crate, +} + +impl ModuleLike { + pub(crate) fn is_crate(self) -> bool { + matches!(self, ModuleLike::Crate) + } +} + #[derive(Template)] #[template(path = "sidebar.html")] pub(super) struct Sidebar<'a> { @@ -530,14 +542,23 @@ fn sidebar_enum<'a>( pub(crate) fn sidebar_module_like( item_sections_in_use: FxHashSet<ItemSection>, ids: &mut IdMap, + module_like: ModuleLike, ) -> LinkBlock<'static> { - let item_sections = ItemSection::ALL + let item_sections: Vec<Link<'_>> = ItemSection::ALL .iter() .copied() .filter(|sec| item_sections_in_use.contains(sec)) .map(|sec| Link::new(ids.derive(sec.id()), sec.name())) .collect(); - LinkBlock::new(Link::empty(), "", item_sections) + let header = if let Some(first_section) = item_sections.get(0) { + Link::new( + first_section.href.to_owned(), + if module_like.is_crate() { "Crate Items" } else { "Module Items" }, + ) + } else { + Link::empty() + }; + LinkBlock::new(header, "", item_sections) } fn sidebar_module(items: &[clean::Item], ids: &mut IdMap) -> LinkBlock<'static> { @@ -561,7 +582,7 @@ fn sidebar_module(items: &[clean::Item], ids: &mut IdMap) -> LinkBlock<'static> .map(|it| item_ty_to_section(it.type_())) .collect(); - sidebar_module_like(item_sections_in_use, ids) + sidebar_module_like(item_sections_in_use, ids, ModuleLike::Module) } fn sidebar_foreign_type<'a>( diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index c0a9b254425..f49cdbabb88 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -17,7 +17,7 @@ {% if !block.heading.name.is_empty() %} <h3> {# #} <a href="#{{block.heading.href|safe}}">{{block.heading.name|wrapped|safe}}</a> {# #} - </h3> {# #} + </h3> {% endif %} {% if !block.links.is_empty() %} <ul class="block{% if !block.class.is_empty() +%} {{+block.class}}{% endif %}"> @@ -25,13 +25,13 @@ <li> {# #} <a href="#{{link.href|safe}}">{{link.name}}</a> {# #} {% if !link.children.is_empty() %} - <ul> {# #} + <ul> {% for child in link.children %} <li><a href="#{{child.href|safe}}">{{child.name}}</a></li> {% endfor %} - </ul> {# #} + </ul> {% endif %} - </li> {# #} + </li> {% endfor %} </ul> {% endif %} @@ -43,7 +43,7 @@ {% if !path.is_empty() %} <h2{% if parent_is_crate +%} class="in-crate"{% endif %}> {# #} <a href="{% if is_mod %}../{% endif %}index.html">In {{+ path|wrapped|safe}}</a> {# #} - </h2> {# #} + </h2> {% endif %} </div> {# #} </div> diff --git a/tests/rustdoc/sidebar/top-toc-nil.rs b/tests/rustdoc/sidebar/top-toc-nil.rs new file mode 100644 index 00000000000..91c7df50ab4 --- /dev/null +++ b/tests/rustdoc/sidebar/top-toc-nil.rs @@ -0,0 +1,7 @@ +#![crate_name = "foo"] + +//! This test case covers missing top TOC entries. + +// @has foo/index.html +// User header +// @!has - '//section[@id="TOC"]/ul[@class="block top-toc"]' 'Basic link and emphasis' From 68773c789a6a84a888b69f7287fd294b6dd3625c Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Mon, 22 Jul 2024 11:12:38 -0700 Subject: [PATCH 04/36] rustdoc: add separate section for module items --- src/librustdoc/html/render/sidebar.rs | 17 ++++++++++++++--- tests/rustdoc/sidebar/module.rs | 16 ++++++++++++++++ tests/rustdoc/sidebar/top-toc-html.rs | 1 + 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 tests/rustdoc/sidebar/module.rs diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index c76253c9fc3..a6b22a18de2 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -26,6 +26,11 @@ impl ModuleLike { matches!(self, ModuleLike::Crate) } } +impl<'a> From<&'a clean::Item> for ModuleLike { + fn from(it: &'a clean::Item) -> ModuleLike { + if it.is_crate() { ModuleLike::Crate } else { ModuleLike::Module } + } +} #[derive(Template)] #[template(path = "sidebar.html")] @@ -119,7 +124,9 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf clean::UnionItem(ref u) => sidebar_union(cx, it, u, &mut blocks), clean::EnumItem(ref e) => sidebar_enum(cx, it, e, &mut blocks), clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t, &mut blocks), - clean::ModuleItem(ref m) => blocks.push(sidebar_module(&m.items, &mut ids)), + clean::ModuleItem(ref m) => { + blocks.push(sidebar_module(&m.items, &mut ids, ModuleLike::from(it))) + } clean::ForeignTypeItem => sidebar_foreign_type(cx, it, &mut blocks), _ => {} } @@ -561,7 +568,11 @@ pub(crate) fn sidebar_module_like( LinkBlock::new(header, "", item_sections) } -fn sidebar_module(items: &[clean::Item], ids: &mut IdMap) -> LinkBlock<'static> { +fn sidebar_module( + items: &[clean::Item], + ids: &mut IdMap, + module_like: ModuleLike, +) -> LinkBlock<'static> { let item_sections_in_use: FxHashSet<_> = items .iter() .filter(|it| { @@ -582,7 +593,7 @@ fn sidebar_module(items: &[clean::Item], ids: &mut IdMap) -> LinkBlock<'static> .map(|it| item_ty_to_section(it.type_())) .collect(); - sidebar_module_like(item_sections_in_use, ids, ModuleLike::Module) + sidebar_module_like(item_sections_in_use, ids, module_like) } fn sidebar_foreign_type<'a>( diff --git a/tests/rustdoc/sidebar/module.rs b/tests/rustdoc/sidebar/module.rs new file mode 100644 index 00000000000..926af71ddfb --- /dev/null +++ b/tests/rustdoc/sidebar/module.rs @@ -0,0 +1,16 @@ +#![crate_name = "foo"] + +//@ has 'foo/index.html' +//@ has - '//section[@id="TOC"]/h3' 'Crate Items' + +//@ has 'foo/bar/index.html' +//@ has - '//section[@id="TOC"]/h3' 'Module Items' +pub mod bar { + //@ has 'foo/bar/struct.Baz.html' + //@ !has - '//section[@id="TOC"]/h3' 'Module Items' + pub struct Baz; +} + +//@ has 'foo/baz/index.html' +//@ !has - '//section[@id="TOC"]/h3' 'Module Items' +pub mod baz {} diff --git a/tests/rustdoc/sidebar/top-toc-html.rs b/tests/rustdoc/sidebar/top-toc-html.rs index 6fc84c1964c..fa1325c8dca 100644 --- a/tests/rustdoc/sidebar/top-toc-html.rs +++ b/tests/rustdoc/sidebar/top-toc-html.rs @@ -14,6 +14,7 @@ // @has foo/index.html // User header +// @has - '//section[@id="TOC"]/h3' 'Sections' // @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]' 'Basic link and emphasis' // @count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]/em' 0 // @count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]/a' 0 From 7091fa5880db84392783147171f5709b72ffa784 Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Mon, 22 Jul 2024 12:59:34 -0700 Subject: [PATCH 05/36] rustdoc: show code spans as `<code>` in TOC --- src/librustdoc/html/markdown.rs | 29 +++++++++++++++++-- src/librustdoc/html/render/sidebar.rs | 12 ++++++-- src/librustdoc/html/templates/sidebar.html | 18 ++++++++++-- src/librustdoc/html/toc.rs | 15 +++++++--- src/librustdoc/html/toc/tests.rs | 6 +++- tests/rustdoc/sidebar/top-toc-html.rs | 15 ++++++---- tests/rustdoc/sidebar/top-toc-idmap.rs | 22 +++++++------- tests/rustdoc/sidebar/top-toc-nil.rs | 4 +-- .../strip-enum-variant.no-not-shown.html | 2 +- 9 files changed, 92 insertions(+), 31 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 58795508a58..4b82f232765 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -50,7 +50,7 @@ use rustc_span::{Span, Symbol}; use crate::clean::RenderedLink; use crate::doctest; use crate::doctest::GlobalTestOptions; -use crate::html::escape::Escape; +use crate::html::escape::{Escape, EscapeBodyText}; use crate::html::format::Buffer; use crate::html::highlight; use crate::html::length_limit::HtmlWithLimit; @@ -535,7 +535,9 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator if let Some(ref mut builder) = self.toc { let mut text_header = String::new(); plain_text_from_events(self.buf.iter().map(|(ev, _)| ev.clone()), &mut text_header); - let sec = builder.push(level as u32, text_header, id.clone()); + let mut html_header = String::new(); + html_text_from_events(self.buf.iter().map(|(ev, _)| ev.clone()), &mut html_header); + let sec = builder.push(level as u32, text_header, html_header, id.clone()); self.buf.push_front((Event::Html(format!("{sec} ").into()), 0..0)); } @@ -1655,6 +1657,29 @@ pub(crate) fn plain_text_from_events<'a>( } } +pub(crate) fn html_text_from_events<'a>( + events: impl Iterator<Item = pulldown_cmark::Event<'a>>, + s: &mut String, +) { + for event in events { + match &event { + Event::Text(text) => { + write!(s, "{}", EscapeBodyText(text)).expect("string alloc infallible") + } + Event::Code(code) => { + s.push_str("<code>"); + write!(s, "{}", EscapeBodyText(code)).expect("string alloc infallible"); + s.push_str("</code>"); + } + Event::HardBreak | Event::SoftBreak => s.push(' '), + Event::Start(Tag::CodeBlock(..)) => break, + Event::End(TagEnd::Paragraph) => break, + Event::End(TagEnd::Heading(..)) => break, + _ => (), + } + } +} + #[derive(Debug)] pub(crate) struct MarkdownLink { pub kind: LinkType, diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index a6b22a18de2..351a23b880d 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -81,8 +81,10 @@ impl<'a> LinkBlock<'a> { /// A link to an item. Content should not be escaped. #[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] pub(crate) struct Link<'a> { - /// The content for the anchor tag + /// The content for the anchor tag and title attr name: Cow<'a, str>, + /// The content for the anchor tag (if different from name) + name_html: Option<Cow<'a, str>>, /// The id of an anchor within the page (without a `#` prefix) href: Cow<'a, str>, /// Nested list of links (used only in top-toc) @@ -91,7 +93,7 @@ pub(crate) struct Link<'a> { impl<'a> Link<'a> { pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self { - Self { href: href.into(), name: name.into(), children: vec![] } + Self { href: href.into(), name: name.into(), children: vec![], name_html: None } } pub fn empty() -> Link<'static> { Link::new("", "") @@ -207,6 +209,7 @@ fn docblock_toc<'a>( .into_iter() .map(|entry| { Link { + name_html: if entry.html == entry.name { None } else { Some(entry.html.into()) }, name: entry.name.into(), href: entry.id.into(), children: entry @@ -214,6 +217,11 @@ fn docblock_toc<'a>( .entries .into_iter() .map(|entry| Link { + name_html: if entry.html == entry.name { + None + } else { + Some(entry.html.into()) + }, name: entry.name.into(), href: entry.id.into(), // Only a single level of nesting is shown here. diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index f49cdbabb88..31823848ec3 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -23,11 +23,25 @@ <ul class="block{% if !block.class.is_empty() +%} {{+block.class}}{% endif %}"> {% for link in block.links %} <li> {# #} - <a href="#{{link.href|safe}}">{{link.name}}</a> {# #} + <a href="#{{link.href|safe}}" title="{{link.name}}"> + {% match link.name_html %} + {% when Some with (html) %} + {{html|safe}} + {% else %} + {{link.name}} + {% endmatch %} + </a> {# #} {% if !link.children.is_empty() %} <ul> {% for child in link.children %} - <li><a href="#{{child.href|safe}}">{{child.name}}</a></li> + <li><a href="#{{child.href|safe}}" title="{{child.name}}"> + {% match child.name_html %} + {% when Some with (html) %} + {{html|safe}} + {% else %} + {{child.name}} + {% endmatch %} + </a></li> {% endfor %} </ul> {% endif %} diff --git a/src/librustdoc/html/toc.rs b/src/librustdoc/html/toc.rs index a44c4a1b59e..7fdce41a634 100644 --- a/src/librustdoc/html/toc.rs +++ b/src/librustdoc/html/toc.rs @@ -1,5 +1,5 @@ //! Table-of-contents creation. -use crate::html::escape::EscapeBodyText; +use crate::html::escape::Escape; /// A (recursive) table of contents #[derive(Debug, PartialEq)] @@ -30,7 +30,12 @@ impl Toc { pub(crate) struct TocEntry { pub(crate) level: u32, pub(crate) sec_number: String, + // name is a plain text header that works in a `title` tag + // html includes `<code>` tags + // the tooltip is used so that, when a toc is truncated, + // you can mouse over it to see the whole thing pub(crate) name: String, + pub(crate) html: String, pub(crate) id: String, pub(crate) children: Toc, } @@ -116,7 +121,7 @@ impl TocBuilder { /// Push a level `level` heading into the appropriate place in the /// hierarchy, returning a string containing the section number in /// `<num>.<num>.<num>` format. - pub(crate) fn push(&mut self, level: u32, name: String, id: String) -> &str { + pub(crate) fn push(&mut self, level: u32, name: String, html: String, id: String) -> &str { assert!(level >= 1); // collapse all previous sections into their parents until we @@ -150,6 +155,7 @@ impl TocBuilder { self.chain.push(TocEntry { level, name, + html, sec_number, id, children: Toc { entries: Vec::new() }, @@ -171,10 +177,11 @@ impl Toc { // recursively format this table of contents let _ = write!( v, - "\n<li><a href=\"#{id}\">{num} {name}</a>", + "\n<li><a href=\"#{id}\" title=\"{name}\">{num} {html}</a>", id = entry.id, num = entry.sec_number, - name = EscapeBodyText(&entry.name) + name = Escape(&entry.name), + html = &entry.html, ); entry.children.print_inner(&mut *v); v.push_str("</li>"); diff --git a/src/librustdoc/html/toc/tests.rs b/src/librustdoc/html/toc/tests.rs index 014f346862b..81aca737baf 100644 --- a/src/librustdoc/html/toc/tests.rs +++ b/src/librustdoc/html/toc/tests.rs @@ -9,7 +9,10 @@ fn builder_smoke() { // there's been no macro mistake. macro_rules! push { ($level: expr, $name: expr) => { - assert_eq!(builder.push($level, $name.to_string(), "".to_string()), $name); + assert_eq!( + builder.push($level, $name.to_string(), $name.to_string(), "".to_string()), + $name + ); }; } push!(2, "0.1"); @@ -48,6 +51,7 @@ fn builder_smoke() { TocEntry { level: $level, name: $name.to_string(), + html: $name.to_string(), sec_number: $name.to_string(), id: "".to_string(), children: toc!($($sub),*) diff --git a/tests/rustdoc/sidebar/top-toc-html.rs b/tests/rustdoc/sidebar/top-toc-html.rs index fa1325c8dca..db998775644 100644 --- a/tests/rustdoc/sidebar/top-toc-html.rs +++ b/tests/rustdoc/sidebar/top-toc-html.rs @@ -4,7 +4,7 @@ #![feature(lazy_type_alias)] #![allow(incomplete_features)] -//! # Basic [link](https://example.com) and *emphasis* +//! # Basic [link](https://example.com) and *emphasis* and `code` //! //! This test case covers TOC entries with rich text inside. //! Rustdoc normally supports headers with links, but for the @@ -12,9 +12,12 @@ //! //! For consistency, emphasis is also filtered out. -// @has foo/index.html +//@ has foo/index.html // User header -// @has - '//section[@id="TOC"]/h3' 'Sections' -// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]' 'Basic link and emphasis' -// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]/em' 0 -// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]/a' 0 +//@ has - '//section[@id="TOC"]/h3' 'Sections' +//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/@title' 'Basic link and emphasis and `code`' +//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]' 'Basic link and emphasis and code' +//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/em' 0 +//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/a' 0 +//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/code' 1 +//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/code' 'code' diff --git a/tests/rustdoc/sidebar/top-toc-idmap.rs b/tests/rustdoc/sidebar/top-toc-idmap.rs index ccfc7336e9f..fdb99fd05a1 100644 --- a/tests/rustdoc/sidebar/top-toc-idmap.rs +++ b/tests/rustdoc/sidebar/top-toc-idmap.rs @@ -14,13 +14,13 @@ //! in the `top-doc`, and the one that's not in the `top-doc` //! needs to match the one that isn't in the `top-toc`. -// @has foo/index.html +//@ has foo/index.html // User header -// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#structs"]' 'Structs' -// @has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="structs"]' 'Structs' +//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#structs"]' 'Structs' +//@ has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="structs"]' 'Structs' // Built-in header -// @has - '//section[@id="TOC"]/ul[@class="block"]/li/a[@href="#structs-1"]' 'Structs' -// @has - '//section[@id="main-content"]/h2[@id="structs-1"]' 'Structs' +//@ has - '//section[@id="TOC"]/ul[@class="block"]/li/a[@href="#structs-1"]' 'Structs' +//@ has - '//section[@id="main-content"]/h2[@id="structs-1"]' 'Structs' /// # Fields /// ## Fields @@ -29,15 +29,15 @@ /// The difference between struct-like headers and module-like headers /// is strange, but not actually a problem as long as we're consistent. -// @has foo/struct.MyStruct.html +//@ has foo/struct.MyStruct.html // User header -// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#fields-1"]' 'Fields' -// @has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="fields-1"]' 'Fields' +//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#fields-1"]' 'Fields' +//@ has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="fields-1"]' 'Fields' // Only one level of nesting -// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]//a' 2 +//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]//a' 2 // Built-in header -// @has - '//section[@id="TOC"]/h3/a[@href="#fields"]' 'Fields' -// @has - '//section[@id="main-content"]/h2[@id="fields"]' 'Fields' +//@ has - '//section[@id="TOC"]/h3/a[@href="#fields"]' 'Fields' +//@ has - '//section[@id="main-content"]/h2[@id="fields"]' 'Fields' pub struct MyStruct { pub fields: i32, diff --git a/tests/rustdoc/sidebar/top-toc-nil.rs b/tests/rustdoc/sidebar/top-toc-nil.rs index 91c7df50ab4..c338bd67fcf 100644 --- a/tests/rustdoc/sidebar/top-toc-nil.rs +++ b/tests/rustdoc/sidebar/top-toc-nil.rs @@ -2,6 +2,6 @@ //! This test case covers missing top TOC entries. -// @has foo/index.html +//@ has foo/index.html // User header -// @!has - '//section[@id="TOC"]/ul[@class="block top-toc"]' 'Basic link and emphasis' +//@ !has - '//section[@id="TOC"]/ul[@class="block top-toc"]' 'Basic link and emphasis' diff --git a/tests/rustdoc/strip-enum-variant.no-not-shown.html b/tests/rustdoc/strip-enum-variant.no-not-shown.html index e072335297d..d7a36cc631a 100644 --- a/tests/rustdoc/strip-enum-variant.no-not-shown.html +++ b/tests/rustdoc/strip-enum-variant.no-not-shown.html @@ -1 +1 @@ -<ul class="block variant"><li><a href="#variant.Shown">Shown</a></li></ul> \ No newline at end of file +<ul class="block variant"><li><a href="#variant.Shown" title="Shown">Shown</a></li></ul> \ No newline at end of file From 95fcddd29f7127bbca70375b8db3d455019843e0 Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Tue, 23 Jul 2024 09:11:52 -0700 Subject: [PATCH 06/36] Add more test case --- tests/rustdoc/sidebar/top-toc-html.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/rustdoc/sidebar/top-toc-html.rs b/tests/rustdoc/sidebar/top-toc-html.rs index db998775644..8991ed0330e 100644 --- a/tests/rustdoc/sidebar/top-toc-html.rs +++ b/tests/rustdoc/sidebar/top-toc-html.rs @@ -4,7 +4,7 @@ #![feature(lazy_type_alias)] #![allow(incomplete_features)] -//! # Basic [link](https://example.com) and *emphasis* and `code` +//! # Basic [link](https://example.com), *emphasis*, **_very emphasis_** and `code` //! //! This test case covers TOC entries with rich text inside. //! Rustdoc normally supports headers with links, but for the @@ -15,9 +15,9 @@ //@ has foo/index.html // User header //@ has - '//section[@id="TOC"]/h3' 'Sections' -//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/@title' 'Basic link and emphasis and `code`' -//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]' 'Basic link and emphasis and code' -//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/em' 0 -//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/a' 0 -//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/code' 1 -//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/code' 'code' +//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/@title' 'Basic link, emphasis, very emphasis and `code`' +//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]' 'Basic link, emphasis, very emphasis and code' +//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/em' 0 +//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/a' 0 +//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/code' 1 +//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/code' 'code' From 12a3c42cccd18d5fadcbad44c3c5442e4aca1a13 Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Wed, 24 Jul 2024 13:16:08 -0700 Subject: [PATCH 07/36] rustdoc: consistentify `#TOC` and `#ModNav` to lowercase --- src/doc/not_found.md | 2 +- src/librustdoc/html/markdown.rs | 5 +++-- src/librustdoc/html/static/css/rustdoc.css | 4 ++-- src/librustdoc/html/static/js/main.js | 4 ++-- src/librustdoc/html/templates/sidebar.html | 4 ++-- tests/rustdoc-gui/sidebar.goml | 24 +++++++++++----------- tests/rustdoc/sidebar/module.rs | 8 ++++---- tests/rustdoc/sidebar/top-toc-html.rs | 14 ++++++------- tests/rustdoc/sidebar/top-toc-idmap.rs | 10 ++++----- tests/rustdoc/sidebar/top-toc-nil.rs | 2 +- 10 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/doc/not_found.md b/src/doc/not_found.md index f0794fc0be3..9552759d2b8 100644 --- a/src/doc/not_found.md +++ b/src/doc/not_found.md @@ -2,7 +2,7 @@ <!-- Completely hide the TOC and the section numbers --> <style type="text/css"> -#TOC { display: none; } +#rustdoc-toc { display: none; } .header-section-number { display: none; } li {list-style-type: none; } #search-input { diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 4b82f232765..5da492f7c6f 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1452,7 +1452,7 @@ impl MarkdownWithToc<'_> { } pub(crate) fn into_string(self) -> String { let (toc, s) = self.into_parts(); - format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.print()) + format!("<nav id=\"rustdoc\">{toc}</nav>{s}", toc = toc.print()) } } @@ -2028,7 +2028,8 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> { map.insert("default-settings".into(), 1); map.insert("sidebar-vars".into(), 1); map.insert("copy-path".into(), 1); - map.insert("TOC".into(), 1); + map.insert("rustdoc-toc".into(), 1); + map.insert("rustdoc-modnav".into(), 1); // This is the list of IDs used by rustdoc sections (but still generated by // rustdoc). map.insert("fields".into(), 1); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index c2bb5fa8f2a..32077e0b7f9 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -589,11 +589,11 @@ ul.block, .block li, .block ul { background-clip: border-box; } -.hide-toc #TOC, .hide-toc .in-crate { +.hide-toc #rustdoc-toc, .hide-toc .in-crate { display: none; } -.hide-modnav #ModNav { +.hide-modnav #rustdoc-modnav { display: none; } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 4135341b22e..5a0796e2353 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -499,7 +499,7 @@ function preLoadCss(cssUrl) { if (!window.SIDEBAR_ITEMS) { return; } - const sidebar = document.getElementById("ModNav"); + const sidebar = document.getElementById("rustdoc-modnav"); /** * Append to the sidebar a "block" of links - a heading along with a list (`<ul>`) of items. @@ -885,7 +885,7 @@ function preLoadCss(cssUrl) { if (!window.ALL_CRATES) { return; } - const sidebarElems = document.getElementById("ModNav"); + const sidebarElems = document.getElementById("rustdoc-modnav"); if (!sidebarElems) { return; } diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index 31823848ec3..fccf65cbefc 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -6,7 +6,7 @@ {% endif %} {% if self.should_render_blocks() %} - <section id="TOC"> + <section id="rustdoc-toc"> {% if !title.is_empty() %} <h2 class="location"> {# #} <a href="#">{{title_prefix}}{{title|wrapped|safe}}</a> {# #} @@ -53,7 +53,7 @@ {% endfor %} </section> {% endif %} - <div id="ModNav"> + <div id="rustdoc-modnav"> {% if !path.is_empty() %} <h2{% if parent_is_crate +%} class="in-crate"{% endif %}> {# #} <a href="{% if is_mod %}../{% endif %}index.html">In {{+ path|wrapped|safe}}</a> {# #} diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml index 3253e6ea86a..7794cdbe9e2 100644 --- a/tests/rustdoc-gui/sidebar.goml +++ b/tests/rustdoc-gui/sidebar.goml @@ -126,8 +126,8 @@ assert-text: (".sidebar-elems ul.block > li.current > a", "module") // - Module name, followed by TOC for module headings // - "In crate [name]" parent pointer, followed by sibling navigation assert-count: (".sidebar h2", 3) -assert-text: (".sidebar > .sidebar-elems > #ModNav > h2", "In crate lib2") -assert-property: (".sidebar > .sidebar-elems > #ModNav > h2 > a", { +assert-text: (".sidebar > .sidebar-elems > #rustdoc-modnav > h2", "In crate lib2") +assert-property: (".sidebar > .sidebar-elems > #rustdoc-modnav > h2 > a", { "href": "/lib2/index.html", }, ENDS_WITH) // We check that we don't have the crate list. @@ -137,8 +137,8 @@ go-to: "./sub_module/sub_sub_module/index.html" assert-property: (".sidebar", {"clientWidth": "200"}) assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2") assert-text: (".sidebar .location", "Module sub_sub_module") -assert-text: (".sidebar > .sidebar-elems > #ModNav > h2", "In lib2::module::sub_module") -assert-property: (".sidebar > .sidebar-elems > #ModNav > h2 > a", { +assert-text: (".sidebar > .sidebar-elems > #rustdoc-modnav > h2", "In lib2::module::sub_module") +assert-property: (".sidebar > .sidebar-elems > #rustdoc-modnav > h2 > a", { "href": "/module/sub_module/index.html", }, ENDS_WITH) assert-text: (".sidebar-elems ul.block > li.current > a", "sub_sub_module") @@ -202,32 +202,32 @@ assert-position: (".sidebar-crate > h2 > a", {"x": -3}) // Configuration option to show TOC in sidebar. set-local-storage: {"rustdoc-hide-toc": "true"} go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" -assert-css: ("#TOC", {"display": "none"}) +assert-css: ("#rustdoc-toc", {"display": "none"}) assert-css: (".sidebar .in-crate", {"display": "none"}) set-local-storage: {"rustdoc-hide-toc": "false"} go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" -assert-css: ("#TOC", {"display": "block"}) +assert-css: ("#rustdoc-toc", {"display": "block"}) assert-css: (".sidebar .in-crate", {"display": "block"}) set-local-storage: {"rustdoc-hide-modnav": "true"} go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" -assert-css: ("#ModNav", {"display": "none"}) +assert-css: ("#rustdoc-modnav", {"display": "none"}) set-local-storage: {"rustdoc-hide-modnav": "false"} go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" -assert-css: ("#ModNav", {"display": "block"}) +assert-css: ("#rustdoc-modnav", {"display": "block"}) set-local-storage: {"rustdoc-hide-toc": "true"} go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -assert-css: ("#TOC", {"display": "none"}) +assert-css: ("#rustdoc-toc", {"display": "none"}) assert-false: ".sidebar .in-crate" set-local-storage: {"rustdoc-hide-toc": "false"} go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -assert-css: ("#TOC", {"display": "block"}) +assert-css: ("#rustdoc-toc", {"display": "block"}) assert-false: ".sidebar .in-crate" set-local-storage: {"rustdoc-hide-modnav": "true"} go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -assert-css: ("#ModNav", {"display": "none"}) +assert-css: ("#rustdoc-modnav", {"display": "none"}) set-local-storage: {"rustdoc-hide-modnav": "false"} go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -assert-css: ("#ModNav", {"display": "block"}) +assert-css: ("#rustdoc-modnav", {"display": "block"}) diff --git a/tests/rustdoc/sidebar/module.rs b/tests/rustdoc/sidebar/module.rs index 926af71ddfb..b5bcb9f232c 100644 --- a/tests/rustdoc/sidebar/module.rs +++ b/tests/rustdoc/sidebar/module.rs @@ -1,16 +1,16 @@ #![crate_name = "foo"] //@ has 'foo/index.html' -//@ has - '//section[@id="TOC"]/h3' 'Crate Items' +//@ has - '//section[@id="rustdoc-toc"]/h3' 'Crate Items' //@ has 'foo/bar/index.html' -//@ has - '//section[@id="TOC"]/h3' 'Module Items' +//@ has - '//section[@id="rustdoc-toc"]/h3' 'Module Items' pub mod bar { //@ has 'foo/bar/struct.Baz.html' - //@ !has - '//section[@id="TOC"]/h3' 'Module Items' + //@ !has - '//section[@id="rustdoc-toc"]/h3' 'Module Items' pub struct Baz; } //@ has 'foo/baz/index.html' -//@ !has - '//section[@id="TOC"]/h3' 'Module Items' +//@ !has - '//section[@id="rustdoc-toc"]/h3' 'Module Items' pub mod baz {} diff --git a/tests/rustdoc/sidebar/top-toc-html.rs b/tests/rustdoc/sidebar/top-toc-html.rs index 8991ed0330e..0f603960434 100644 --- a/tests/rustdoc/sidebar/top-toc-html.rs +++ b/tests/rustdoc/sidebar/top-toc-html.rs @@ -14,10 +14,10 @@ //@ has foo/index.html // User header -//@ has - '//section[@id="TOC"]/h3' 'Sections' -//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/@title' 'Basic link, emphasis, very emphasis and `code`' -//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]' 'Basic link, emphasis, very emphasis and code' -//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/em' 0 -//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/a' 0 -//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/code' 1 -//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/code' 'code' +//@ has - '//section[@id="rustdoc-toc"]/h3' 'Sections' +//@ has - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/@title' 'Basic link, emphasis, very emphasis and `code`' +//@ has - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]' 'Basic link, emphasis, very emphasis and code' +//@ count - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/em' 0 +//@ count - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/a' 0 +//@ count - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/code' 1 +//@ has - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-emphasis-very-emphasis-and-code"]/code' 'code' diff --git a/tests/rustdoc/sidebar/top-toc-idmap.rs b/tests/rustdoc/sidebar/top-toc-idmap.rs index fdb99fd05a1..af07cb4179b 100644 --- a/tests/rustdoc/sidebar/top-toc-idmap.rs +++ b/tests/rustdoc/sidebar/top-toc-idmap.rs @@ -16,10 +16,10 @@ //@ has foo/index.html // User header -//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#structs"]' 'Structs' +//@ has - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]/li/a[@href="#structs"]' 'Structs' //@ has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="structs"]' 'Structs' // Built-in header -//@ has - '//section[@id="TOC"]/ul[@class="block"]/li/a[@href="#structs-1"]' 'Structs' +//@ has - '//section[@id="rustdoc-toc"]/ul[@class="block"]/li/a[@href="#structs-1"]' 'Structs' //@ has - '//section[@id="main-content"]/h2[@id="structs-1"]' 'Structs' /// # Fields @@ -31,12 +31,12 @@ //@ has foo/struct.MyStruct.html // User header -//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#fields-1"]' 'Fields' +//@ has - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]/li/a[@href="#fields-1"]' 'Fields' //@ has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="fields-1"]' 'Fields' // Only one level of nesting -//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]//a' 2 +//@ count - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]//a' 2 // Built-in header -//@ has - '//section[@id="TOC"]/h3/a[@href="#fields"]' 'Fields' +//@ has - '//section[@id="rustdoc-toc"]/h3/a[@href="#fields"]' 'Fields' //@ has - '//section[@id="main-content"]/h2[@id="fields"]' 'Fields' pub struct MyStruct { diff --git a/tests/rustdoc/sidebar/top-toc-nil.rs b/tests/rustdoc/sidebar/top-toc-nil.rs index c338bd67fcf..d72d41abf88 100644 --- a/tests/rustdoc/sidebar/top-toc-nil.rs +++ b/tests/rustdoc/sidebar/top-toc-nil.rs @@ -4,4 +4,4 @@ //@ has foo/index.html // User header -//@ !has - '//section[@id="TOC"]/ul[@class="block top-toc"]' 'Basic link and emphasis' +//@ !has - '//section[@id="rustdoc-toc"]/ul[@class="block top-toc"]' 'Basic link and emphasis' From bead042d5e8831a2648efbebac3cf62c6a87fdea Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Wed, 24 Jul 2024 13:34:18 -0700 Subject: [PATCH 08/36] rustdoc: add test case for modnav position when TOC is off --- src/librustdoc/html/render/context.rs | 7 +-- src/librustdoc/html/render/sidebar.rs | 11 +++-- .../rustdoc-gui/sidebar-modnav-position.goml | 44 +++++++++++++++++++ 3 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 tests/rustdoc-gui/sidebar-modnav-position.goml diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 510da99c9ef..72fa9ef706c 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -14,12 +14,9 @@ use rustc_span::edition::Edition; use rustc_span::{sym, FileName, Symbol}; use super::print_item::{full_path, item_path, print_item}; +use super::sidebar::{print_sidebar, sidebar_module_like, ModuleLike, Sidebar}; use super::write_shared::write_shared; -use super::{ - collect_spans_and_sources, scrape_examples_help, - sidebar::{print_sidebar, sidebar_module_like, ModuleLike, Sidebar}, - AllTypes, LinkFromSrc, StylePath, -}; +use super::{collect_spans_and_sources, scrape_examples_help, AllTypes, LinkFromSrc, StylePath}; use crate::clean::types::ExternalLocation; use crate::clean::utils::has_doc_flag; use crate::clean::{self, ExternalCrate}; diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 351a23b880d..fe64739731c 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -7,13 +7,12 @@ use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefIdSet; use rustc_middle::ty::{self, TyCtxt}; -use crate::{ - clean, - formats::{item_type::ItemType, Impl}, - html::{format::Buffer, markdown::IdMap, markdown::MarkdownWithToc}, -}; - use super::{item_ty_to_section, Context, ItemSection}; +use crate::clean; +use crate::formats::item_type::ItemType; +use crate::formats::Impl; +use crate::html::format::Buffer; +use crate::html::markdown::{IdMap, MarkdownWithToc}; #[derive(Clone, Copy)] pub(crate) enum ModuleLike { diff --git a/tests/rustdoc-gui/sidebar-modnav-position.goml b/tests/rustdoc-gui/sidebar-modnav-position.goml new file mode 100644 index 00000000000..eb86d118ab2 --- /dev/null +++ b/tests/rustdoc-gui/sidebar-modnav-position.goml @@ -0,0 +1,44 @@ +// Verifies that, when TOC is hidden, modnav is always in exactly the same spot +// This is driven by a reasonably common use case: +// +// - There are three or more items that might meet my needs. +// - I open the first one, decide it's not what I want, switch to the second one using the sidebar. +// - The second one also doesn't meet my needs, so I switch to the third. +// - The third also doesn't meet my needs, so... +// +// because the sibling module nav is in exactly the same place every time, +// it's very easy to find and switch between pages that way. + +go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" +show-text: true +set-local-storage: {"rustdoc-hide-toc": "true"} + +define-function: ( + "check-positions", + [url], + block { + go-to: "file://" + |DOC_PATH| + |url| + // Checking results colors. + assert-position: ("#rustdoc-modnav > h2", {"x": |h2_x|, "y": |h2_y|}) + assert-position: ( + "#rustdoc-modnav > ul:first-of-type > li:first-of-type", + {"x": |x|, "y": |y|} + ) + }, +) + +// First, at test_docs root +go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html" +store-position: ("#rustdoc-modnav > h2", {"x": h2_x, "y": h2_y}) +store-position: ("#rustdoc-modnav > ul:first-of-type > li:first-of-type", {"x": x, "y": y}) +call-function: ("check-positions", {"url": "/test_docs/enum.WhoLetTheDogOut.html"}) +call-function: ("check-positions", {"url": "/test_docs/struct.StructWithPublicUndocumentedFields.html"}) +call-function: ("check-positions", {"url": "/test_docs/codeblock_sub/index.html"}) + +// Now in a submodule +go-to: "file://" + |DOC_PATH| + "/test_docs/fields/struct.Struct.html" +store-position: ("#rustdoc-modnav > h2", {"x": h2_x, "y": h2_y}) +store-position: ("#rustdoc-modnav > ul:first-of-type > li:first-of-type", {"x": x, "y": y}) +call-function: ("check-positions", {"url": "/test_docs/fields/struct.Struct.html"}) +call-function: ("check-positions", {"url": "/test_docs/fields/union.Union.html"}) +call-function: ("check-positions", {"url": "/test_docs/fields/enum.Enum.html"}) From e77eb042cef5a0035a13e69072a5c8c8fc45f1af Mon Sep 17 00:00:00 2001 From: Adrian Taylor <adetaylor@chromium.org> Date: Sat, 30 Mar 2024 19:25:22 +0000 Subject: [PATCH 09/36] Arbitrary self types v2: pointers feature gate. The main `arbitrary_self_types` feature gate will shortly be reused for a new version of arbitrary self types which we are amending per [this RFC](https://github.com/rust-lang/rfcs/blob/master/text/3519-arbitrary-self-types-v2.md). The main amendments are: * _do_ support `self` types which can't safely implement `Deref` * do _not_ support generic `self` types * do _not_ support raw pointers as `self` types. This PR relates to the last of those bullet points: this strips pointer support from the current `arbitrary_self_types` feature. We expect this to cause some amount of breakage for crates using this unstable feature to allow raw pointer self types. If that's the case, we want to know about it, and we want crate authors to know of the upcoming changes. For now, this can be resolved by adding the new `arbitrary_self_types_pointers` feature to such crates. If we determine that use of raw pointers as self types is common, then we may maintain that as an unstable feature even if we come to stabilize the rest of the `arbitrary_self_types` support in future. If we don't hear that this PR is causing breakage, then perhaps we don't need it at all, even behind an unstable feature gate. [Tracking issue](https://github.com/rust-lang/rust/issues/44874) This is [step 4 of the plan outlined here](https://github.com/rust-lang/rust/issues/44874#issuecomment-2122179688) --- compiler/rustc_feature/src/unstable.rs | 4 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 84 ++++++++++++++----- compiler/rustc_hir_typeck/src/method/probe.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + .../miri/tests/pass/dyn-arbitrary-self.rs | 2 +- .../building/receiver_ptr_mutability.rs | 2 +- ...r-to-trait-obj-different-regions-lt-ext.rs | 2 +- ...ture-gate-arbitrary-self-types-pointers.rs | 15 ++++ ...-gate-arbitrary-self-types-pointers.stderr | 36 ++++++++ ...te-arbitrary_self_types-raw-pointer.stderr | 12 +-- .../auxiliary/inference_unstable_iterator.rs | 2 +- .../auxiliary/inference_unstable_itertools.rs | 2 +- ...arbitrary_self_types_raw_pointer_struct.rs | 2 +- .../arbitrary_self_types_raw_pointer_trait.rs | 2 +- 14 files changed, 134 insertions(+), 34 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-arbitrary-self-types-pointers.rs create mode 100644 tests/ui/feature-gates/feature-gate-arbitrary-self-types-pointers.stderr diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 4524458023d..4197c89c011 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -348,8 +348,10 @@ declare_features! ( (unstable, adt_const_params, "1.56.0", Some(95174)), /// Allows defining an `#[alloc_error_handler]`. (unstable, alloc_error_handler, "1.29.0", Some(51540)), - /// Allows trait methods with arbitrary self types. + /// Allows inherent and trait methods with arbitrary self types. (unstable, arbitrary_self_types, "1.23.0", Some(44874)), + /// Allows inherent and trait methods with arbitrary self types that are raw pointers. + (unstable, arbitrary_self_types_pointers, "CURRENT_RUSTC_VERSION", Some(44874)), /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), /// Allows using `label` operands in inline assembly. diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index fb9bcc113c6..2ed31d837a8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1651,6 +1651,14 @@ fn check_fn_or_method<'tcx>( } } +/// The `arbitrary_self_types_pointers` feature implies `arbitrary_self_types`. +#[derive(Clone, Copy)] +enum ArbitrarySelfTypesLevel { + None, // neither arbitrary_self_types nor arbitrary_self_types_pointers + ArbitrarySelfTypes, // just arbitrary_self_types + ArbitrarySelfTypesPointers, // both arbitrary_self_types and arbitrary_self_types_pointers +} + #[instrument(level = "debug", skip(wfcx))] fn check_method_receiver<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, @@ -1683,14 +1691,28 @@ fn check_method_receiver<'tcx>( return Ok(()); } - if tcx.features().arbitrary_self_types { - if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { - // Report error; `arbitrary_self_types` was enabled. - return Err(tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })); - } + let arbitrary_self_types_level = if tcx.features().arbitrary_self_types_pointers { + ArbitrarySelfTypesLevel::ArbitrarySelfTypesPointers + } else if tcx.features().arbitrary_self_types { + ArbitrarySelfTypesLevel::ArbitrarySelfTypes } else { - if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, false) { - return Err(if receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { + ArbitrarySelfTypesLevel::None + }; + + if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level) { + return Err(match arbitrary_self_types_level { + // Wherever possible, emit a message advising folks that the features + // `arbitrary_self_types` or `arbitrary_self_types_pointers` might + // have helped. + ArbitrarySelfTypesLevel::None + if receiver_is_valid( + wfcx, + span, + receiver_ty, + self_ty, + ArbitrarySelfTypesLevel::ArbitrarySelfTypes, + ) => + { // Report error; would have worked with `arbitrary_self_types`. feature_err( &tcx.sess, @@ -1698,25 +1720,49 @@ fn check_method_receiver<'tcx>( span, format!( "`{receiver_ty}` cannot be used as the type of `self` without \ - the `arbitrary_self_types` feature", + the `arbitrary_self_types` feature", ), ) .with_help(fluent::hir_analysis_invalid_receiver_ty_help) .emit() - } else { - // Report error; would not have worked with `arbitrary_self_types`. + } + ArbitrarySelfTypesLevel::ArbitrarySelfTypes | ArbitrarySelfTypesLevel::None + if receiver_is_valid( + wfcx, + span, + receiver_ty, + self_ty, + ArbitrarySelfTypesLevel::ArbitrarySelfTypesPointers, + ) => + { + // Report error; would have worked with `arbitrary_self_types_pointers`. + feature_err( + &tcx.sess, + sym::arbitrary_self_types_pointers, + span, + format!( + "`{receiver_ty}` cannot be used as the type of `self` without \ + the `arbitrary_self_types_pointers` feature", + ), + ) + .with_help(fluent::hir_analysis_invalid_receiver_ty_help) + .emit() + } + _ => + // Report error; would not have worked with `arbitrary_self_types[_pointers]`. + { tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty }) - }); - } + } + }); } Ok(()) } /// Returns whether `receiver_ty` would be considered a valid receiver type for `self_ty`. If /// `arbitrary_self_types` is enabled, `receiver_ty` must transitively deref to `self_ty`, possibly -/// through a `*const/mut T` raw pointer. If the feature is not enabled, the requirements are more -/// strict: `receiver_ty` must implement `Receiver` and directly implement -/// `Deref<Target = self_ty>`. +/// through a `*const/mut T` raw pointer if `arbitrary_self_types_pointers` is also enabled. +/// If neither feature is enabled, the requirements are more strict: `receiver_ty` must implement +/// `Receiver` and directly implement `Deref<Target = self_ty>`. /// /// N.B., there are cases this function returns `true` but causes an error to be emitted, /// particularly when `receiver_ty` derefs to a type that is the same as `self_ty` but has the @@ -1726,7 +1772,7 @@ fn receiver_is_valid<'tcx>( span: Span, receiver_ty: Ty<'tcx>, self_ty: Ty<'tcx>, - arbitrary_self_types_enabled: bool, + arbitrary_self_types_enabled: ArbitrarySelfTypesLevel, ) -> bool { let infcx = wfcx.infcx; let tcx = wfcx.tcx(); @@ -1744,8 +1790,8 @@ fn receiver_is_valid<'tcx>( let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty); - // The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`. - if arbitrary_self_types_enabled { + // The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`. + if matches!(arbitrary_self_types_enabled, ArbitrarySelfTypesLevel::ArbitrarySelfTypesPointers) { autoderef = autoderef.include_raw_pointers(); } @@ -1771,7 +1817,7 @@ fn receiver_is_valid<'tcx>( // Without `feature(arbitrary_self_types)`, we require that each step in the // deref chain implement `receiver`. - if !arbitrary_self_types_enabled { + if matches!(arbitrary_self_types_enabled, ArbitrarySelfTypesLevel::None) { if !receiver_is_implemented( wfcx, receiver_trait_def_id, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 28f537c87c4..e557cbf1701 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -402,7 +402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mode, })); } else if bad_ty.reached_raw_pointer - && !self.tcx.features().arbitrary_self_types + && !self.tcx.features().arbitrary_self_types_pointers && !self.tcx.sess.at_least_rust_2018() { // this case used to be allowed by the compiler, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2957105288b..84c87b62262 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -406,6 +406,7 @@ symbols! { append_const_msg, arbitrary_enum_discriminant, arbitrary_self_types, + arbitrary_self_types_pointers, args, arith_offset, arm, diff --git a/src/tools/miri/tests/pass/dyn-arbitrary-self.rs b/src/tools/miri/tests/pass/dyn-arbitrary-self.rs index 6be13b155f4..dc2b9e4491b 100644 --- a/src/tools/miri/tests/pass/dyn-arbitrary-self.rs +++ b/src/tools/miri/tests/pass/dyn-arbitrary-self.rs @@ -1,6 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -#![feature(arbitrary_self_types, unsize, coerce_unsized, dispatch_from_dyn)] +#![feature(arbitrary_self_types_pointers, unsize, coerce_unsized, dispatch_from_dyn)] #![feature(rustc_attrs)] fn pin_box_dyn() { diff --git a/tests/mir-opt/building/receiver_ptr_mutability.rs b/tests/mir-opt/building/receiver_ptr_mutability.rs index 4bb3b4cade5..1ddb8b71a5a 100644 --- a/tests/mir-opt/building/receiver_ptr_mutability.rs +++ b/tests/mir-opt/building/receiver_ptr_mutability.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR receiver_ptr_mutability.main.built.after.mir -#![feature(arbitrary_self_types)] +#![feature(arbitrary_self_types_pointers)] struct Test {} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs index 96345de01c9..be2b89aab08 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs @@ -2,7 +2,7 @@ // // issue: <https://github.com/rust-lang/rust/issues/120217> -#![feature(arbitrary_self_types)] +#![feature(arbitrary_self_types_pointers)] trait Static<'a> { fn proof(self: *const Self, s: &'a str) -> &'static str; diff --git a/tests/ui/feature-gates/feature-gate-arbitrary-self-types-pointers.rs b/tests/ui/feature-gates/feature-gate-arbitrary-self-types-pointers.rs new file mode 100644 index 00000000000..79ceb05662b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-arbitrary-self-types-pointers.rs @@ -0,0 +1,15 @@ +trait Foo { + fn foo(self: *const Self); //~ ERROR `*const Self` cannot be used as the type of `self` +} + +struct Bar; + +impl Foo for Bar { + fn foo(self: *const Self) {} //~ ERROR `*const Bar` cannot be used as the type of `self` +} + +impl Bar { + fn bar(self: *mut Self) {} //~ ERROR `*mut Bar` cannot be used as the type of `self` +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-arbitrary-self-types-pointers.stderr b/tests/ui/feature-gates/feature-gate-arbitrary-self-types-pointers.stderr new file mode 100644 index 00000000000..3bb93cf2ea0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-arbitrary-self-types-pointers.stderr @@ -0,0 +1,36 @@ +error[E0658]: `*const Bar` cannot be used as the type of `self` without the `arbitrary_self_types_pointers` feature + --> $DIR/feature-gate-arbitrary-self-types-pointers.rs:8:18 + | +LL | fn foo(self: *const Self) {} + | ^^^^^^^^^^^ + | + = note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information + = help: add `#![feature(arbitrary_self_types_pointers)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) + +error[E0658]: `*mut Bar` cannot be used as the type of `self` without the `arbitrary_self_types_pointers` feature + --> $DIR/feature-gate-arbitrary-self-types-pointers.rs:12:18 + | +LL | fn bar(self: *mut Self) {} + | ^^^^^^^^^ + | + = note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information + = help: add `#![feature(arbitrary_self_types_pointers)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) + +error[E0658]: `*const Self` cannot be used as the type of `self` without the `arbitrary_self_types_pointers` feature + --> $DIR/feature-gate-arbitrary-self-types-pointers.rs:2:18 + | +LL | fn foo(self: *const Self); + | ^^^^^^^^^^^ + | + = note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information + = help: add `#![feature(arbitrary_self_types_pointers)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-arbitrary_self_types-raw-pointer.stderr b/tests/ui/feature-gates/feature-gate-arbitrary_self_types-raw-pointer.stderr index 711025ff93b..856e0595331 100644 --- a/tests/ui/feature-gates/feature-gate-arbitrary_self_types-raw-pointer.stderr +++ b/tests/ui/feature-gates/feature-gate-arbitrary_self_types-raw-pointer.stderr @@ -1,33 +1,33 @@ -error[E0658]: `*const Foo` cannot be used as the type of `self` without the `arbitrary_self_types` feature +error[E0658]: `*const Foo` cannot be used as the type of `self` without the `arbitrary_self_types_pointers` feature --> $DIR/feature-gate-arbitrary_self_types-raw-pointer.rs:4:18 | LL | fn foo(self: *const Self) {} | ^^^^^^^^^^^ | = note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information - = help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable + = help: add `#![feature(arbitrary_self_types_pointers)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) -error[E0658]: `*const ()` cannot be used as the type of `self` without the `arbitrary_self_types` feature +error[E0658]: `*const ()` cannot be used as the type of `self` without the `arbitrary_self_types_pointers` feature --> $DIR/feature-gate-arbitrary_self_types-raw-pointer.rs:14:18 | LL | fn bar(self: *const Self) {} | ^^^^^^^^^^^ | = note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information - = help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable + = help: add `#![feature(arbitrary_self_types_pointers)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) -error[E0658]: `*const Self` cannot be used as the type of `self` without the `arbitrary_self_types` feature +error[E0658]: `*const Self` cannot be used as the type of `self` without the `arbitrary_self_types_pointers` feature --> $DIR/feature-gate-arbitrary_self_types-raw-pointer.rs:9:18 | LL | fn bar(self: *const Self); | ^^^^^^^^^^^ | = note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information - = help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable + = help: add `#![feature(arbitrary_self_types_pointers)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) diff --git a/tests/ui/inference/auxiliary/inference_unstable_iterator.rs b/tests/ui/inference/auxiliary/inference_unstable_iterator.rs index 04bc0b1a8ac..8ba6fc89f16 100644 --- a/tests/ui/inference/auxiliary/inference_unstable_iterator.rs +++ b/tests/ui/inference/auxiliary/inference_unstable_iterator.rs @@ -1,5 +1,5 @@ #![feature(staged_api)] -#![feature(arbitrary_self_types)] +#![feature(arbitrary_self_types_pointers)] #![stable(feature = "ipu_iterator", since = "1.0.0")] diff --git a/tests/ui/inference/auxiliary/inference_unstable_itertools.rs b/tests/ui/inference/auxiliary/inference_unstable_itertools.rs index fa1efbcfefc..32ca3a45119 100644 --- a/tests/ui/inference/auxiliary/inference_unstable_itertools.rs +++ b/tests/ui/inference/auxiliary/inference_unstable_itertools.rs @@ -1,4 +1,4 @@ -#![feature(arbitrary_self_types)] +#![feature(arbitrary_self_types_pointers)] pub trait IpuItertools { fn ipu_flatten(&self) -> u32 { diff --git a/tests/ui/self/arbitrary_self_types_raw_pointer_struct.rs b/tests/ui/self/arbitrary_self_types_raw_pointer_struct.rs index 1f45d91847f..7f76ed7fd2a 100644 --- a/tests/ui/self/arbitrary_self_types_raw_pointer_struct.rs +++ b/tests/ui/self/arbitrary_self_types_raw_pointer_struct.rs @@ -1,5 +1,5 @@ //@ run-pass -#![feature(arbitrary_self_types)] +#![feature(arbitrary_self_types_pointers)] use std::rc::Rc; diff --git a/tests/ui/self/arbitrary_self_types_raw_pointer_trait.rs b/tests/ui/self/arbitrary_self_types_raw_pointer_trait.rs index 43f596659b9..6f34c9281b0 100644 --- a/tests/ui/self/arbitrary_self_types_raw_pointer_trait.rs +++ b/tests/ui/self/arbitrary_self_types_raw_pointer_trait.rs @@ -1,5 +1,5 @@ //@ run-pass -#![feature(arbitrary_self_types)] +#![feature(arbitrary_self_types_pointers)] use std::ptr; From 94e9c4cd64f9f30e46be5a149f90205d7096c104 Mon Sep 17 00:00:00 2001 From: binarycat <binarycat@envs.net> Date: Sun, 25 Aug 2024 13:25:27 -0400 Subject: [PATCH 10/36] warn the user if the upstream master branch is old fixes https://github.com/rust-lang/rust/issues/129528 --- src/bootstrap/src/core/build_steps/format.rs | 5 +-- src/tools/build_helper/src/git.rs | 34 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 8c52df78ab6..1e78583476a 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -7,7 +7,7 @@ use std::sync::mpsc::SyncSender; use std::sync::Mutex; use build_helper::ci::CiEnv; -use build_helper::git::get_git_modified_files; +use build_helper::git::{get_git_modified_files, warn_old_master_branch}; use ignore::WalkBuilder; use crate::core::builder::Builder; @@ -93,7 +93,8 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, Str if !verify_rustfmt_version(build) { return Ok(None); } - + warn_old_master_branch(&build.config.git_config(), &build.config.src) + .map_err(|e| e.to_string())?; get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"]) } diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs index da84569c778..cc48a8964a3 100644 --- a/src/tools/build_helper/src/git.rs +++ b/src/tools/build_helper/src/git.rs @@ -159,3 +159,37 @@ pub fn get_git_untracked_files( .collect(); Ok(Some(files)) } + +/// Print a warning if the branch returned from `updated_master_branch` is old +/// +/// For certain configurations of git repository, this remote will not be +/// updated when running `git pull`. +/// +/// This can result in formatting thousands of files instead of a dozen, +/// so we should warn the user something is wrong. +pub fn warn_old_master_branch( + config: &GitConfig<'_>, + git_dir: &Path, +) -> Result<(), Box<dyn std::error::Error>> { + use std::time::Duration; + const WARN_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 10); + let updated_master = updated_master_branch(config, Some(git_dir))?; + let branch_path = git_dir.join(".git/refs/remotes").join(&updated_master); + match std::fs::metadata(branch_path) { + Ok(meta) => { + if meta.modified()?.elapsed()? > WARN_AFTER { + eprintln!("warning: {updated_master} has not been updated in 10 days"); + } else { + return Ok(()); + } + } + Err(err) => { + eprintln!("warning: unable to check if {updated_master} is old due to error: {err}") + } + } + eprintln!( + "warning: {updated_master} is used to determine if files have been modified\n\ + warning: if it is not updated, this may cause files to be needlessly reformatted" + ); + Ok(()) +} From cea707d96033d5e14d3c2b287b0bedd2847db680 Mon Sep 17 00:00:00 2001 From: binarycat <binarycat@envs.net> Date: Tue, 27 Aug 2024 16:31:40 -0400 Subject: [PATCH 11/36] emit old upstream warning no matter the build step --- src/bootstrap/src/core/build_steps/format.rs | 4 +--- src/bootstrap/src/core/sanity.rs | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 1e78583476a..91fbc57429a 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -7,7 +7,7 @@ use std::sync::mpsc::SyncSender; use std::sync::Mutex; use build_helper::ci::CiEnv; -use build_helper::git::{get_git_modified_files, warn_old_master_branch}; +use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; use crate::core::builder::Builder; @@ -93,8 +93,6 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, Str if !verify_rustfmt_version(build) { return Ok(None); } - warn_old_master_branch(&build.config.git_config(), &build.config.src) - .map_err(|e| e.to_string())?; get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"]) } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index c42d4c56c38..37b88ca4fa5 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -15,6 +15,8 @@ use std::ffi::{OsStr, OsString}; use std::path::PathBuf; use std::{env, fs}; +use build_helper::git::warn_old_master_branch; + #[cfg(not(feature = "bootstrap-self-test"))] use crate::builder::Builder; use crate::builder::Kind; @@ -379,4 +381,8 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake if let Some(ref s) = build.config.ccache { cmd_finder.must_have(s); } + + warn_old_master_branch(&build.config.git_config(), &build.config.src) + .map_err(|e| e.to_string()) + .unwrap(); } From 3743cdb0e2d93f61c934c8fc73aa13dc8e9b8ab1 Mon Sep 17 00:00:00 2001 From: binarycat <binarycat@envs.net> Date: Tue, 27 Aug 2024 19:15:46 -0400 Subject: [PATCH 12/36] downgrade git error to a warning, and skip UX check in CI --- src/bootstrap/src/core/sanity.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 37b88ca4fa5..cb076624820 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -382,7 +382,13 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake cmd_finder.must_have(s); } - warn_old_master_branch(&build.config.git_config(), &build.config.src) - .map_err(|e| e.to_string()) - .unwrap(); + // this warning is useless in CI, + // and CI probably won't have the right branches anyway. + if !build_helper::ci::CiEnv::is_ci() { + if let Err(e) = warn_old_master_branch(&build.config.git_config(), &build.config.src) + .map_err(|e| e.to_string()) + { + eprintln!("unable to check if upstream branch is old: {e}"); + } + } } From 992dd0bbe39fc65257fd9e8bfd21a1d0779ec52e Mon Sep 17 00:00:00 2001 From: Miguel Ojeda <ojeda@kernel.org> Date: Sat, 10 Aug 2024 14:56:22 +0200 Subject: [PATCH 13/36] CI: rfl: add a couple comments to split the steps and document them Signed-off-by: Miguel Ojeda <ojeda@kernel.org> --- src/ci/docker/scripts/rfl-build.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 389abb2fdd3..90706a55017 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -62,11 +62,20 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ defconfig \ rfl-for-rust-ci.config +# Build a few Rust targets +# +# This does not include building the C side of the kernel nor linking, +# which can find other issues, but it is much faster. +# +# This includes transforming `rustdoc` tests into KUnit ones thanks to +# `CONFIG_RUST_KERNEL_DOCTESTS=y` above (which, for the moment, uses the +# unstable `--test-builder` and `--no-run`). make -C linux LLVM=1 -j$(($(nproc) + 1)) \ samples/rust/rust_minimal.o \ samples/rust/rust_print.o \ drivers/net/phy/ax88796b_rust.o \ rust/doctests_kernel_generated.o +# Generate documentation make -C linux LLVM=1 -j$(($(nproc) + 1)) \ rustdoc From d783b1dabdcf7e61612d9ab7dde35f38251d3736 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda <ojeda@kernel.org> Date: Wed, 28 Aug 2024 01:53:47 +0200 Subject: [PATCH 14/36] CI: rfl: switch to a stage 2 build Apparently tools like `rustfmt` require it in order to find the right `librustc_driver.so` without extra tweaks. Signed-off-by: Miguel Ojeda <ojeda@kernel.org> --- src/ci/docker/scripts/rfl-build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 90706a55017..51644f2f47e 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -5,7 +5,7 @@ set -euo pipefail LINUX_VERSION=4c7864e81d8bbd51036dacf92fb0a400e13aaeee # Build rustc, rustdoc and cargo -../x.py build --stage 1 library rustdoc +../x.py build --stage 2 library rustdoc ../x.py build --stage 0 cargo # Install rustup so that we can use the built toolchain easily, and also @@ -16,7 +16,7 @@ sh rustup.sh -y --default-toolchain none source /cargo/env BUILD_DIR=$(realpath ./build) -rustup toolchain link local "${BUILD_DIR}"/x86_64-unknown-linux-gnu/stage1 +rustup toolchain link local "${BUILD_DIR}"/x86_64-unknown-linux-gnu/stage2 rustup default local mkdir -p rfl From 824397ddf6536664ec91ac42c6473b7cbf8844f6 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda <ojeda@kernel.org> Date: Sat, 10 Aug 2024 16:35:52 +0200 Subject: [PATCH 15/36] CI: rfl: add a Clippy build Signed-off-by: Miguel Ojeda <ojeda@kernel.org> --- src/ci/docker/scripts/rfl-build.sh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 51644f2f47e..2f9ec3c23a8 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -4,8 +4,8 @@ set -euo pipefail LINUX_VERSION=4c7864e81d8bbd51036dacf92fb0a400e13aaeee -# Build rustc, rustdoc and cargo -../x.py build --stage 2 library rustdoc +# Build rustc, rustdoc, cargo and clippy-driver +../x.py build --stage 2 library rustdoc clippy ../x.py build --stage 0 cargo # Install rustup so that we can use the built toolchain easily, and also @@ -79,3 +79,14 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ # Generate documentation make -C linux LLVM=1 -j$(($(nproc) + 1)) \ rustdoc + +# Re-build with Clippy enabled +# +# This should not introduce Clippy errors, since `CONFIG_WERROR` is not +# set (thus no `-Dwarnings`) and the kernel uses `-W` for all Clippy +# lints, including `clippy::all`. However, it could catch ICEs. +make -C linux LLVM=1 -j$(($(nproc) + 1)) CLIPPY=1 \ + samples/rust/rust_minimal.o \ + samples/rust/rust_print.o \ + drivers/net/phy/ax88796b_rust.o \ + rust/doctests_kernel_generated.o From d584f7099a3c17c76a38f686a9af0b1d36a686ab Mon Sep 17 00:00:00 2001 From: Miguel Ojeda <ojeda@kernel.org> Date: Sat, 10 Aug 2024 16:47:20 +0200 Subject: [PATCH 16/36] CI: rfl: add a `rustfmt` run This change will also remove the current warnings in the build due to `rustfmt` not being available (for `bindgen` output): error: 'rustfmt' is not installed for the custom toolchain 'local'. note: this is a custom toolchain, which cannot use `rustup component add` help: if you built this toolchain from source, and used `rustup toolchain link`, then you may be able to build the component with `x.py` Failed to run rustfmt: Internal rustfmt error (non-fatal, continuing) Signed-off-by: Miguel Ojeda <ojeda@kernel.org> --- src/ci/docker/scripts/rfl-build.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 2f9ec3c23a8..030b6f6ee17 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -4,8 +4,8 @@ set -euo pipefail LINUX_VERSION=4c7864e81d8bbd51036dacf92fb0a400e13aaeee -# Build rustc, rustdoc, cargo and clippy-driver -../x.py build --stage 2 library rustdoc clippy +# Build rustc, rustdoc, cargo, clippy-driver and rustfmt +../x.py build --stage 2 library rustdoc clippy rustfmt ../x.py build --stage 0 cargo # Install rustup so that we can use the built toolchain easily, and also @@ -90,3 +90,10 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) CLIPPY=1 \ samples/rust/rust_print.o \ drivers/net/phy/ax88796b_rust.o \ rust/doctests_kernel_generated.o + +# Format the code +# +# This returns successfully even if there were changes, i.e. it is not +# a check. +make -C linux LLVM=1 -j$(($(nproc) + 1)) \ + rustfmt From 5c558d3ff419fce1d74dd455bb089e26938578af Mon Sep 17 00:00:00 2001 From: Miguel Ojeda <ojeda@kernel.org> Date: Sat, 10 Aug 2024 17:08:44 +0200 Subject: [PATCH 17/36] CI: rfl: add macro expanded source build (`-Zunpretty=expanded`) This particular target does not expand into much code, so it is a good first candidate to see if we could keep this in the CI. Signed-off-by: Miguel Ojeda <ojeda@kernel.org> --- src/ci/docker/scripts/rfl-build.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 030b6f6ee17..e8b6235921d 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -80,6 +80,14 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ rustdoc +# Build macro expanded source (`-Zunpretty=expanded`) +# +# This target also formats the macro expanded code, thus it is also +# intended to catch ICEs with formatting `-Zunpretty=expanded` output +# like https://github.com/rust-lang/rustfmt/issues/6105. +make -C linux LLVM=1 -j$(($(nproc) + 1)) \ + samples/rust/rust_minimal.rsi + # Re-build with Clippy enabled # # This should not introduce Clippy errors, since `CONFIG_WERROR` is not From 778ff06ba6fe5d5c85604e569179100e00dd24d1 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda <ojeda@kernel.org> Date: Sat, 10 Aug 2024 17:22:42 +0200 Subject: [PATCH 18/36] CI: rfl: factor out build targets It will make it easier to add more in the future. Signed-off-by: Miguel Ojeda <ojeda@kernel.org> --- src/ci/docker/scripts/rfl-build.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index e8b6235921d..8011e07e92e 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -62,6 +62,13 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ defconfig \ rfl-for-rust-ci.config +BUILD_TARGETS=" + samples/rust/rust_minimal.o + samples/rust/rust_print.o + drivers/net/phy/ax88796b_rust.o + rust/doctests_kernel_generated.o +" + # Build a few Rust targets # # This does not include building the C side of the kernel nor linking, @@ -71,10 +78,7 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ # `CONFIG_RUST_KERNEL_DOCTESTS=y` above (which, for the moment, uses the # unstable `--test-builder` and `--no-run`). make -C linux LLVM=1 -j$(($(nproc) + 1)) \ - samples/rust/rust_minimal.o \ - samples/rust/rust_print.o \ - drivers/net/phy/ax88796b_rust.o \ - rust/doctests_kernel_generated.o + $BUILD_TARGETS # Generate documentation make -C linux LLVM=1 -j$(($(nproc) + 1)) \ @@ -94,10 +98,7 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ # set (thus no `-Dwarnings`) and the kernel uses `-W` for all Clippy # lints, including `clippy::all`. However, it could catch ICEs. make -C linux LLVM=1 -j$(($(nproc) + 1)) CLIPPY=1 \ - samples/rust/rust_minimal.o \ - samples/rust/rust_print.o \ - drivers/net/phy/ax88796b_rust.o \ - rust/doctests_kernel_generated.o + $BUILD_TARGETS # Format the code # From 8e20b66524a9de9109f3cfae551e470641238ccf Mon Sep 17 00:00:00 2001 From: Adrian Taylor <adetaylor@chromium.org> Date: Fri, 30 Aug 2024 16:57:05 +0000 Subject: [PATCH 19/36] Some code review suggestions. --- .../rustc_hir_analysis/src/check/wfcheck.rs | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 2ed31d837a8..21a7f6da111 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1652,11 +1652,10 @@ fn check_fn_or_method<'tcx>( } /// The `arbitrary_self_types_pointers` feature implies `arbitrary_self_types`. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] enum ArbitrarySelfTypesLevel { - None, // neither arbitrary_self_types nor arbitrary_self_types_pointers - ArbitrarySelfTypes, // just arbitrary_self_types - ArbitrarySelfTypesPointers, // both arbitrary_self_types and arbitrary_self_types_pointers + Basic, // just arbitrary_self_types + WithPointers, // both arbitrary_self_types and arbitrary_self_types_pointers } #[instrument(level = "debug", skip(wfcx))] @@ -1692,11 +1691,11 @@ fn check_method_receiver<'tcx>( } let arbitrary_self_types_level = if tcx.features().arbitrary_self_types_pointers { - ArbitrarySelfTypesLevel::ArbitrarySelfTypesPointers + Some(ArbitrarySelfTypesLevel::WithPointers) } else if tcx.features().arbitrary_self_types { - ArbitrarySelfTypesLevel::ArbitrarySelfTypes + Some(ArbitrarySelfTypesLevel::Basic) } else { - ArbitrarySelfTypesLevel::None + None }; if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level) { @@ -1704,14 +1703,13 @@ fn check_method_receiver<'tcx>( // Wherever possible, emit a message advising folks that the features // `arbitrary_self_types` or `arbitrary_self_types_pointers` might // have helped. - ArbitrarySelfTypesLevel::None - if receiver_is_valid( - wfcx, - span, - receiver_ty, - self_ty, - ArbitrarySelfTypesLevel::ArbitrarySelfTypes, - ) => + None if receiver_is_valid( + wfcx, + span, + receiver_ty, + self_ty, + Some(ArbitrarySelfTypesLevel::Basic), + ) => { // Report error; would have worked with `arbitrary_self_types`. feature_err( @@ -1726,13 +1724,13 @@ fn check_method_receiver<'tcx>( .with_help(fluent::hir_analysis_invalid_receiver_ty_help) .emit() } - ArbitrarySelfTypesLevel::ArbitrarySelfTypes | ArbitrarySelfTypesLevel::None + None | Some(ArbitrarySelfTypesLevel::Basic) if receiver_is_valid( wfcx, span, receiver_ty, self_ty, - ArbitrarySelfTypesLevel::ArbitrarySelfTypesPointers, + Some(ArbitrarySelfTypesLevel::WithPointers), ) => { // Report error; would have worked with `arbitrary_self_types_pointers`. @@ -1772,7 +1770,7 @@ fn receiver_is_valid<'tcx>( span: Span, receiver_ty: Ty<'tcx>, self_ty: Ty<'tcx>, - arbitrary_self_types_enabled: ArbitrarySelfTypesLevel, + arbitrary_self_types_enabled: Option<ArbitrarySelfTypesLevel>, ) -> bool { let infcx = wfcx.infcx; let tcx = wfcx.tcx(); @@ -1791,7 +1789,7 @@ fn receiver_is_valid<'tcx>( let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty); // The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`. - if matches!(arbitrary_self_types_enabled, ArbitrarySelfTypesLevel::ArbitrarySelfTypesPointers) { + if arbitrary_self_types_enabled == Some(ArbitrarySelfTypesLevel::WithPointers) { autoderef = autoderef.include_raw_pointers(); } @@ -1817,7 +1815,7 @@ fn receiver_is_valid<'tcx>( // Without `feature(arbitrary_self_types)`, we require that each step in the // deref chain implement `receiver`. - if matches!(arbitrary_self_types_enabled, ArbitrarySelfTypesLevel::None) { + if arbitrary_self_types_enabled.is_none() { if !receiver_is_implemented( wfcx, receiver_trait_def_id, From 121e9f4cc86104da0209afd3f681dc82da9e3928 Mon Sep 17 00:00:00 2001 From: The 8472 <git@infinite-source.de> Date: Fri, 2 Sep 2022 20:15:26 +0200 Subject: [PATCH 20/36] Add rust.randomize-layout config to build artifacts with -Zrandomize-layout Additionally teach compiletest to ignore tests that rely on deterministic layout. Tests themselves aren't built with randomization but they can still observe slight changes in std or rustc --- config.example.toml | 3 +++ src/bootstrap/src/core/build_steps/test.rs | 3 +++ src/bootstrap/src/core/builder.rs | 4 ++++ src/bootstrap/src/core/config/config.rs | 7 +++++++ src/tools/compiletest/src/command-list.rs | 1 + src/tools/compiletest/src/common.rs | 3 +++ src/tools/compiletest/src/header/needs.rs | 5 +++++ src/tools/compiletest/src/lib.rs | 6 ++++++ 8 files changed, 32 insertions(+) diff --git a/config.example.toml b/config.example.toml index f1dc32234cc..897857f286c 100644 --- a/config.example.toml +++ b/config.example.toml @@ -512,6 +512,9 @@ # are disabled statically" because `max_level_info` is enabled, set this value to `true`. #debug-logging = rust.debug-assertions (boolean) +# Whether or not to build rustc, tools and the libraries with randomized type layout +#randomize-layout = false + # Whether or not overflow checks are enabled for the compiler and standard # library. # diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 84a6b26a491..a8e12540473 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1810,6 +1810,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the if builder.config.rust_optimize_tests { cmd.arg("--optimize-tests"); } + if builder.config.rust_randomize_layout { + cmd.arg("--rust-randomized-layout"); + } if builder.config.cmd.only_modified() { cmd.arg("--only-modified"); } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index ff0d1f3a725..5df962e939e 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1614,6 +1614,10 @@ impl<'a> Builder<'a> { rustflags.arg("-Csymbol-mangling-version=legacy"); } + if self.config.rust_randomize_layout { + rustflags.arg("-Zrandomize-layout"); + } + // Enable compile-time checking of `cfg` names, values and Cargo `features`. // // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index bdfee55d8d1..547e338e049 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -280,6 +280,7 @@ pub struct Config { pub rust_codegen_backends: Vec<String>, pub rust_verify_llvm_ir: bool, pub rust_thin_lto_import_instr_limit: Option<u32>, + pub rust_randomize_layout: bool, pub rust_remap_debuginfo: bool, pub rust_new_symbol_mangling: Option<bool>, pub rust_profile_use: Option<String>, @@ -1088,6 +1089,7 @@ define_config! { codegen_units: Option<u32> = "codegen-units", codegen_units_std: Option<u32> = "codegen-units-std", debug_assertions: Option<bool> = "debug-assertions", + randomize_layout: Option<bool> = "randomize-layout", debug_assertions_std: Option<bool> = "debug-assertions-std", overflow_checks: Option<bool> = "overflow-checks", overflow_checks_std: Option<bool> = "overflow-checks-std", @@ -1179,6 +1181,7 @@ impl Config { backtrace: true, rust_optimize: RustOptimize::Bool(true), rust_optimize_tests: true, + rust_randomize_layout: false, submodules: None, docs: true, docs_minification: true, @@ -1629,6 +1632,7 @@ impl Config { backtrace, incremental, parallel_compiler, + randomize_layout, default_linker, channel, description, @@ -1718,6 +1722,7 @@ impl Config { set(&mut config.lld_mode, lld_mode); set(&mut config.llvm_bitcode_linker_enabled, llvm_bitcode_linker); + config.rust_randomize_layout = randomize_layout.unwrap_or_default(); config.llvm_tools_enabled = llvm_tools.unwrap_or(true); config.rustc_parallel = parallel_compiler.unwrap_or(config.channel == "dev" || config.channel == "nightly"); @@ -2889,6 +2894,7 @@ fn check_incompatible_options_for_ci_rustc( let Rust { // Following options are the CI rustc incompatible ones. optimize, + randomize_layout, debug_logging, debuginfo_level_rustc, llvm_tools, @@ -2953,6 +2959,7 @@ fn check_incompatible_options_for_ci_rustc( // otherwise, we just print a warning with `warn` macro. err!(current_rust_config.optimize, optimize); + err!(current_rust_config.randomize_layout, randomize_layout); err!(current_rust_config.debug_logging, debug_logging); err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc); err!(current_rust_config.rpath, rpath); diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs index a559d6f81a2..865aa76ddb0 100644 --- a/src/tools/compiletest/src/command-list.rs +++ b/src/tools/compiletest/src/command-list.rs @@ -136,6 +136,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "min-llvm-version", "min-system-llvm-version", "needs-asm-support", + "needs-deterministic-layouts", "needs-dlltool", "needs-dynamic-linking", "needs-force-clang-based-tests", diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 5831f7c3cf2..773d795f75a 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -274,6 +274,9 @@ pub struct Config { /// Flags to pass to the compiler when building for the target pub target_rustcflags: Vec<String>, + /// Whether the compiler and stdlib has been built with randomized struct layouts + pub rust_randomized_layout: bool, + /// Whether tests should be optimized by default. Individual test-suites and test files may /// override this setting. pub optimize_tests: bool, diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index 72b1b9c6d48..e903f60ceb3 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -134,6 +134,11 @@ pub(super) fn handle_needs( condition: config.target_cfg().relocation_model == "pic", ignore_reason: "ignored on targets without PIC relocation model", }, + Need { + name: "needs-deterministic-layouts", + condition: !config.rust_randomized_layout, + ignore_reason: "ignored when randomizing layouts", + }, Need { name: "needs-wasmtime", condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 7018362af54..5402e69bc66 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -99,6 +99,11 @@ pub fn parse_config(args: Vec<String>) -> Config { ) .optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS") .optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS") + .optflag( + "", + "rust-randomized-layout", + "set this when rustc/stdlib were compiled with randomized layouts", + ) .optflag("", "optimize-tests", "run tests with optimizations enabled") .optflag("", "verbose", "run tests verbosely, showing all output") .optflag( @@ -286,6 +291,7 @@ pub fn parse_config(args: Vec<String>) -> Config { host_rustcflags: matches.opt_strs("host-rustcflags"), target_rustcflags: matches.opt_strs("target-rustcflags"), optimize_tests: matches.opt_present("optimize-tests"), + rust_randomized_layout: matches.opt_present("rust-randomized-layout"), target, host: opt_str2(matches.opt_str("host")), cdb, From f3bc08adbd120a12571373b982b39049cbbed93f Mon Sep 17 00:00:00 2001 From: The 8472 <git@infinite-source.de> Date: Fri, 2 Sep 2022 20:15:53 +0200 Subject: [PATCH 21/36] ignore/fix layout-sensitive tests --- tests/codegen/issues/issue-86106.rs | 1 + tests/codegen/mem-replace-big-type.rs | 1 + tests/codegen/slice-iter-nonnull.rs | 1 + tests/codegen/vecdeque-drain.rs | 1 + .../mir-opt/pre-codegen/issue_117368_print_invalid_constant.rs | 1 + tests/ui/stats/hir-stats.rs | 3 +++ tests/ui/structs-enums/type-sizes.rs | 1 + 7 files changed, 9 insertions(+) diff --git a/tests/codegen/issues/issue-86106.rs b/tests/codegen/issues/issue-86106.rs index e8164c5c380..8d1ce116d26 100644 --- a/tests/codegen/issues/issue-86106.rs +++ b/tests/codegen/issues/issue-86106.rs @@ -1,5 +1,6 @@ //@ only-64bit llvm appears to use stores instead of memset on 32bit //@ compile-flags: -C opt-level=3 -Z merge-functions=disabled +//@ needs-deterministic-layouts // The below two functions ensure that both `String::new()` and `"".to_string()` // produce the identical code. diff --git a/tests/codegen/mem-replace-big-type.rs b/tests/codegen/mem-replace-big-type.rs index d5eadda4469..e62f1a953df 100644 --- a/tests/codegen/mem-replace-big-type.rs +++ b/tests/codegen/mem-replace-big-type.rs @@ -5,6 +5,7 @@ //@ compile-flags: -C no-prepopulate-passes -Zinline-mir=no //@ ignore-debug: precondition checks in ptr::read make them a bad candidate for MIR inlining +//@ needs-deterministic-layouts #![crate_type = "lib"] diff --git a/tests/codegen/slice-iter-nonnull.rs b/tests/codegen/slice-iter-nonnull.rs index c960688b00c..eda807d3682 100644 --- a/tests/codegen/slice-iter-nonnull.rs +++ b/tests/codegen/slice-iter-nonnull.rs @@ -1,4 +1,5 @@ //@ compile-flags: -O +//@ needs-deterministic-layouts #![crate_type = "lib"] #![feature(exact_size_is_empty)] diff --git a/tests/codegen/vecdeque-drain.rs b/tests/codegen/vecdeque-drain.rs index 31fcf035f11..fca1ed367e6 100644 --- a/tests/codegen/vecdeque-drain.rs +++ b/tests/codegen/vecdeque-drain.rs @@ -1,6 +1,7 @@ // Check that draining at the front or back doesn't copy memory. //@ compile-flags: -O +//@ needs-deterministic-layouts //@ ignore-debug: FIXME: checks for call detect scoped noalias metadata #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.rs b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.rs index c92424f2983..08347f71b42 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.rs +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.rs @@ -1,3 +1,4 @@ +//@needs-deterministic-layouts // Verify that we do not ICE when printing an invalid constant. // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/ui/stats/hir-stats.rs b/tests/ui/stats/hir-stats.rs index 249413d80e8..7c5da8cf554 100644 --- a/tests/ui/stats/hir-stats.rs +++ b/tests/ui/stats/hir-stats.rs @@ -1,12 +1,15 @@ //@ check-pass //@ compile-flags: -Zhir-stats //@ only-x86_64 +// layout randomization affects the hir stat output +//@ needs-deterministic-layouts // Type layouts sometimes change. When that happens, until the next bootstrap // bump occurs, stage1 and stage2 will give different outputs for this test. // Add an `ignore-stage1` comment marker to work around that problem during // that time. + // The aim here is to include at least one of every different type of top-level // AST/HIR node reported by `-Zhir-stats`. diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs index 9c933a9ef1c..5ca9c8678b7 100644 --- a/tests/ui/structs-enums/type-sizes.rs +++ b/tests/ui/structs-enums/type-sizes.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ needs-deterministic-layouts #![allow(non_camel_case_types)] #![allow(dead_code)] From 5bf8eeb9f34a738ebf49328c08d40951374cce18 Mon Sep 17 00:00:00 2001 From: The 8472 <git@infinite-source.de> Date: Fri, 2 Sep 2022 20:17:20 +0200 Subject: [PATCH 22/36] disable size asserts in the compiler when randomizing layouts --- Cargo.lock | 1 + compiler/rustc/Cargo.toml | 1 + compiler/rustc_driver_impl/Cargo.toml | 5 +++++ compiler/rustc_index/Cargo.toml | 1 + compiler/rustc_index/src/lib.rs | 11 +++++++++++ compiler/rustc_middle/Cargo.toml | 1 + compiler/rustc_middle/src/query/plumbing.rs | 1 + src/bootstrap/src/lib.rs | 3 +++ 8 files changed, 24 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 6ea4cd8f5ac..ec3b95d0d98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3569,6 +3569,7 @@ dependencies = [ "rustc_hir_pretty", "rustc_hir_typeck", "rustc_incremental", + "rustc_index", "rustc_infer", "rustc_interface", "rustc_lint", diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 5008069542f..a2fc9d5c408 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -30,5 +30,6 @@ features = ['unprefixed_malloc_on_supported_platforms'] jemalloc = ['dep:jemalloc-sys'] llvm = ['rustc_driver_impl/llvm'] max_level_info = ['rustc_driver_impl/max_level_info'] +rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] rustc_use_parallel_compiler = ['rustc_driver_impl/rustc_use_parallel_compiler'] # tidy-alphabetical-end diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 91cbffcd707..6d6d3f35a4b 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -23,6 +23,7 @@ rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } rustc_incremental = { path = "../rustc_incremental" } +rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } rustc_interface = { path = "../rustc_interface" } rustc_lint = { path = "../rustc_lint" } @@ -72,6 +73,10 @@ ctrlc = "3.4.4" # tidy-alphabetical-start llvm = ['rustc_interface/llvm'] max_level_info = ['rustc_log/max_level_info'] +rustc_randomized_layouts = [ + 'rustc_index/rustc_randomized_layouts', + 'rustc_middle/rustc_randomized_layouts' +] rustc_use_parallel_compiler = [ 'rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler', diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml index 92ea3f278dc..f7d18f84e34 100644 --- a/compiler/rustc_index/Cargo.toml +++ b/compiler/rustc_index/Cargo.toml @@ -20,4 +20,5 @@ nightly = [ "dep:rustc_macros", "rustc_index_macros/nightly", ] +rustc_randomized_layouts = [] # tidy-alphabetical-end diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index f773b5b46ad..52f354b8eca 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -33,8 +33,19 @@ pub use vec::IndexVec; /// /// </div> #[macro_export] +#[cfg(not(feature = "rustc_randomized_layouts"))] macro_rules! static_assert_size { ($ty:ty, $size:expr) => { const _: [(); $size] = [(); ::std::mem::size_of::<$ty>()]; }; } + +#[macro_export] +#[cfg(feature = "rustc_randomized_layouts")] +macro_rules! static_assert_size { + ($ty:ty, $size:expr) => { + // no effect other than using the statements. + // struct sizes are not deterministic under randomized layouts + const _: (usize, usize) = ($size, ::std::mem::size_of::<$ty>()); + }; +} diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 69e3b703cce..b23589afb58 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -40,5 +40,6 @@ tracing = "0.1" [features] # tidy-alphabetical-start +rustc_randomized_layouts = [] rustc_use_parallel_compiler = ["dep:rustc-rayon-core"] # tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index c9bd702cce3..0320a91d142 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -337,6 +337,7 @@ macro_rules! define_callbacks { // Ensure that values grow no larger than 64 bytes by accident. // Increase this limit if necessary, but do try to keep the size low if possible #[cfg(target_pointer_width = "64")] + #[cfg(not(feature = "rustc_randomized_layouts"))] const _: () = { if mem::size_of::<Value<'static>>() > 64 { panic!("{}", concat!( diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 268392c5fb1..dc852e894a0 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -678,6 +678,9 @@ impl Build { if self.config.rustc_parallel { features.push("rustc_use_parallel_compiler"); } + if self.config.rust_randomize_layout { + features.push("rustc_randomized_layouts"); + } // If debug logging is on, then we want the default for tracing: // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26 From e3169f76e6597bc3b582070436ce513b5db2dab8 Mon Sep 17 00:00:00 2001 From: The 8472 <git@infinite-source.de> Date: Fri, 2 Sep 2022 20:18:19 +0200 Subject: [PATCH 23/36] when -Zrandomize-layout is enabled disable alloc test testing internal struct sizes --- library/alloc/Cargo.toml | 1 + library/alloc/src/collections/btree/node/tests.rs | 2 +- src/bootstrap/src/core/builder.rs | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 4365bcc4ad0..1bd4434d4f7 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -52,4 +52,5 @@ check-cfg = [ 'cfg(no_global_oom_handling)', 'cfg(no_rc)', 'cfg(no_sync)', + 'cfg(randomized_layouts)', ] diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index d230749d712..4d2fa0f0941 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -90,7 +90,7 @@ fn test_partial_eq() { #[test] #[cfg(target_arch = "x86_64")] -#[cfg_attr(miri, ignore)] // We'd like to run Miri with layout randomization +#[cfg_attr(any(miri, randomized_layouts), ignore)] // We'd like to run Miri with layout randomization fn test_sizes() { assert_eq!(core::mem::size_of::<LeafNode<(), ()>>(), 16); assert_eq!(core::mem::size_of::<LeafNode<i64, i64>>(), 16 + CAPACITY * 2 * 8); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 5df962e939e..3043fd09ed1 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -2193,6 +2193,9 @@ impl<'a> Builder<'a> { rustflags.arg("-Zvalidate-mir"); rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}")); } + if self.config.rust_randomize_layout { + rustflags.arg("--cfg=randomized_layouts"); + } // Always enable inlining MIR when building the standard library. // Without this flag, MIR inlining is disabled when incremental compilation is enabled. // That causes some mir-opt tests which inline functions from the standard library to From 1e377c5b9fd820d97d7fa3f0447c1c45b5fb64c7 Mon Sep 17 00:00:00 2001 From: The 8472 <git@infinite-source.de> Date: Fri, 2 Sep 2022 22:44:39 +0200 Subject: [PATCH 24/36] enable layout randomization in x86_64-gnu-llvm-17 CI job --- src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile index 275acb47c33..3acc2ceb135 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile @@ -50,6 +50,7 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --llvm-root=/usr/lib/llvm-17 \ --enable-llvm-link-shared \ + --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ From df20808f4dc7828c0f78cd17c3bd9edbd499d6f3 Mon Sep 17 00:00:00 2001 From: The 8472 <git@infinite-source.de> Date: Sat, 31 Aug 2024 14:43:23 +0200 Subject: [PATCH 25/36] inhibit layout randomization for Box --- compiler/rustc_abi/src/layout.rs | 11 +++++++---- compiler/rustc_abi/src/lib.rs | 15 +++++++++++---- compiler/rustc_middle/src/ty/mod.rs | 10 +++++++++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 5160b4ed0a2..7432768be4a 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -968,8 +968,8 @@ fn univariant< let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut max_repr_align = repr.align; let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect(); - let optimize = !repr.inhibit_struct_field_reordering(); - if optimize && fields.len() > 1 { + let optimize_field_order = !repr.inhibit_struct_field_reordering(); + if optimize_field_order && fields.len() > 1 { let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; let optimizing = &mut inverse_memory_index.raw[..end]; let fields_excluding_tail = &fields.raw[..end]; @@ -1176,7 +1176,7 @@ fn univariant< // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. // Field 5 would be the first element, so memory_index is i: // Note: if we didn't optimize, it's already right. - let memory_index = if optimize { + let memory_index = if optimize_field_order { inverse_memory_index.invert_bijective_mapping() } else { debug_assert!(inverse_memory_index.iter().copied().eq(fields.indices())); @@ -1189,6 +1189,9 @@ fn univariant< } let mut layout_of_single_non_zst_field = None; let mut abi = Abi::Aggregate { sized }; + + let optimize_abi = !repr.inhibit_newtype_abi_optimization(); + // Try to make this a Scalar/ScalarPair. if sized && size.bytes() > 0 { // We skip *all* ZST here and later check if we are good in terms of alignment. @@ -1205,7 +1208,7 @@ fn univariant< match field.abi { // For plain scalars, or vectors of them, we can't unpack // newtypes for `#[repr(C)]`, as that affects C ABIs. - Abi::Scalar(_) | Abi::Vector { .. } if optimize => { + Abi::Scalar(_) | Abi::Vector { .. } if optimize_abi => { abi = field.abi; } // But scalar pairs are Rust-specific and get diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index df29b3d54f0..be42bc84932 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -43,14 +43,17 @@ bitflags! { const IS_SIMD = 1 << 1; const IS_TRANSPARENT = 1 << 2; // Internal only for now. If true, don't reorder fields. + // On its own it does not prevent ABI optimizations. const IS_LINEAR = 1 << 3; - // If true, the type's layout can be randomized using - // the seed stored in `ReprOptions.field_shuffle_seed` + // If true, the type's crate has opted into layout randomization. + // Other flags can still inhibit reordering and thus randomization. + // The seed stored in `ReprOptions.field_shuffle_seed`. const RANDOMIZE_LAYOUT = 1 << 4; // Any of these flags being set prevent field reordering optimisation. - const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits() + const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits() | ReprFlags::IS_LINEAR.bits(); + const ABI_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits(); } } @@ -139,10 +142,14 @@ impl ReprOptions { self.c() || self.int.is_some() } + pub fn inhibit_newtype_abi_optimization(&self) -> bool { + self.flags.intersects(ReprFlags::ABI_UNOPTIMIZABLE) + } + /// Returns `true` if this `#[repr()]` guarantees a fixed field order, /// e.g. `repr(C)` or `repr(<int>)`. pub fn inhibit_struct_field_reordering(&self) -> bool { - self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some() + self.flags.intersects(ReprFlags::FIELD_ORDER_UNOPTIMIZABLE) || self.int.is_some() } /// Returns `true` if this type is valid for reordering and `-Z randomize-layout` diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e41ea7507ef..874e75f7179 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -35,6 +35,7 @@ use rustc_data_structures::tagged_ptr::CopyTaggedPtr; use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; +use rustc_hir::LangItem; use rustc_index::IndexVec; use rustc_macros::{ extension, Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, @@ -1570,8 +1571,15 @@ impl<'tcx> TyCtxt<'tcx> { flags.insert(ReprFlags::RANDOMIZE_LAYOUT); } + // box is special, on the one hand the compiler assumes an ordered layout, with the pointer + // always at offset zero. On the other hand we want scalar abi optimizations. + let is_box = self.is_lang_item(did.to_def_id(), LangItem::OwnedBox); + // This is here instead of layout because the choice must make it into metadata. - if !self.consider_optimizing(|| format!("Reorder fields of {:?}", self.def_path_str(did))) { + if is_box + || !self + .consider_optimizing(|| format!("Reorder fields of {:?}", self.def_path_str(did))) + { flags.insert(ReprFlags::IS_LINEAR); } From c218c754568351e44a8c8f07702c090c6c2ae945 Mon Sep 17 00:00:00 2001 From: The 8472 <git@infinite-source.de> Date: Sat, 31 Aug 2024 18:02:01 +0200 Subject: [PATCH 26/36] exclude tools with deps that have size asserts --- src/bootstrap/src/core/builder.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 3043fd09ed1..a4474046fb8 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1614,7 +1614,12 @@ impl<'a> Builder<'a> { rustflags.arg("-Csymbol-mangling-version=legacy"); } - if self.config.rust_randomize_layout { + // FIXME: the following components don't build with `-Zrandomize-layout` yet: + // - wasm-component-ld, due to the `wast`crate + // - rust-analyzer, due to the rowan crate + // so we exclude entire categories of steps here due to lack of fine-grained control over + // rustflags. + if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc { rustflags.arg("-Zrandomize-layout"); } From 8d3e5fa0ae31a4b00ef7dc8d895e12865af08747 Mon Sep 17 00:00:00 2001 From: Kevin Mehall <km@kevinmehall.net> Date: Mon, 2 Sep 2024 17:38:51 -0600 Subject: [PATCH 27/36] Move the `data` and `vtable` methods from `RawWaker` to `Waker` Per the `waker_getters` FCP: https://github.com/rust-lang/rust/issues/96992#issuecomment-1941998046 --- library/core/src/task/wake.rs | 44 +++++++++++++++++------------------ library/core/tests/waker.rs | 11 ++++----- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 7e5c1574f53..9baaa274bb0 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -60,22 +60,6 @@ impl RawWaker { RawWaker { data, vtable } } - /// Gets the `data` pointer used to create this `RawWaker`. - #[inline] - #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] - pub fn data(&self) -> *const () { - self.data - } - - /// Gets the `vtable` pointer used to create this `RawWaker`. - #[inline] - #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] - pub fn vtable(&self) -> &'static RawWakerVTable { - self.vtable - } - #[unstable(feature = "noop_waker", issue = "98286")] const NOOP: RawWaker = { const VTABLE: RawWakerVTable = RawWakerVTable::new( @@ -565,12 +549,20 @@ impl Waker { WAKER } - /// Gets a reference to the underlying [`RawWaker`]. + /// Gets the `data` pointer used to create this `Waker`. #[inline] #[must_use] #[unstable(feature = "waker_getters", issue = "96992")] - pub fn as_raw(&self) -> &RawWaker { - &self.waker + pub fn data(&self) -> *const () { + self.waker.data + } + + /// Gets the `vtable` pointer used to create this `Waker`. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "96992")] + pub fn vtable(&self) -> &'static RawWakerVTable { + self.waker.vtable } } @@ -831,12 +823,20 @@ impl LocalWaker { WAKER } - /// Gets a reference to the underlying [`RawWaker`]. + /// Gets the `data` pointer used to create this `LocalWaker`. #[inline] #[must_use] #[unstable(feature = "waker_getters", issue = "96992")] - pub fn as_raw(&self) -> &RawWaker { - &self.waker + pub fn data(&self) -> *const () { + self.waker.data + } + + /// Gets the `vtable` pointer used to create this `LocalWaker`. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "96992")] + pub fn vtable(&self) -> &'static RawWakerVTable { + self.waker.vtable } } #[unstable(feature = "local_waker", issue = "118959")] diff --git a/library/core/tests/waker.rs b/library/core/tests/waker.rs index 361e900e695..8f6bf0565fc 100644 --- a/library/core/tests/waker.rs +++ b/library/core/tests/waker.rs @@ -4,14 +4,13 @@ use std::task::{RawWaker, RawWakerVTable, Waker}; #[test] fn test_waker_getters() { let raw_waker = RawWaker::new(ptr::without_provenance_mut(42usize), &WAKER_VTABLE); - assert_eq!(raw_waker.data() as usize, 42); - assert!(ptr::eq(raw_waker.vtable(), &WAKER_VTABLE)); - let waker = unsafe { Waker::from_raw(raw_waker) }; + assert_eq!(waker.data() as usize, 42); + assert!(ptr::eq(waker.vtable(), &WAKER_VTABLE)); + let waker2 = waker.clone(); - let raw_waker2 = waker2.as_raw(); - assert_eq!(raw_waker2.data() as usize, 43); - assert!(ptr::eq(raw_waker2.vtable(), &WAKER_VTABLE)); + assert_eq!(waker2.data() as usize, 43); + assert!(ptr::eq(waker2.vtable(), &WAKER_VTABLE)); } static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( From 2dc75148ee4ac7e6edfc25911b28b877e1bed0f6 Mon Sep 17 00:00:00 2001 From: Kevin Mehall <km@kevinmehall.net> Date: Mon, 2 Sep 2024 17:42:50 -0600 Subject: [PATCH 28/36] Stabilize waker_getters --- library/core/src/task/wake.rs | 8 ++++---- library/core/tests/lib.rs | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 9baaa274bb0..8dc7e33aed3 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -552,7 +552,7 @@ impl Waker { /// Gets the `data` pointer used to create this `Waker`. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] + #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] pub fn data(&self) -> *const () { self.waker.data } @@ -560,7 +560,7 @@ impl Waker { /// Gets the `vtable` pointer used to create this `Waker`. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] + #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] pub fn vtable(&self) -> &'static RawWakerVTable { self.waker.vtable } @@ -826,7 +826,7 @@ impl LocalWaker { /// Gets the `data` pointer used to create this `LocalWaker`. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] + #[unstable(feature = "local_waker", issue = "118959")] pub fn data(&self) -> *const () { self.waker.data } @@ -834,7 +834,7 @@ impl LocalWaker { /// Gets the `vtable` pointer used to create this `LocalWaker`. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] + #[unstable(feature = "local_waker", issue = "118959")] pub fn vtable(&self) -> &'static RawWakerVTable { self.waker.vtable } diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index c205f028dd3..96fc621494f 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -112,7 +112,6 @@ #![feature(unsize)] #![feature(unsized_tuple_coercion)] #![feature(unwrap_infallible)] -#![feature(waker_getters)] // tidy-alphabetical-end #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] From 22bd31977294f0c948409ec24034895f14b59ade Mon Sep 17 00:00:00 2001 From: Kevin Mehall <km@kevinmehall.net> Date: Mon, 2 Sep 2024 18:25:09 -0600 Subject: [PATCH 29/36] Add `Waker::new` and `LocalWaker::new` Per the `waker_getters` FCP: https://github.com/rust-lang/rust/issues/96992#issuecomment-1941998046 Docs largely copied from `RawWaker::new`. --- library/core/src/task/wake.rs | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 8dc7e33aed3..6924b3c13ec 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -493,6 +493,37 @@ impl Waker { a_data == b_data && ptr::eq(a_vtable, b_vtable) } + /// Creates a new `Waker` from the provided `data` pointer and `vtable`. + /// + /// The `data` pointer can be used to store arbitrary data as required + /// by the executor. This could be e.g. a type-erased pointer to an `Arc` + /// that is associated with the task. + /// The value of this pointer will get passed to all functions that are part + /// of the `vtable` as the first parameter. + /// + /// It is important to consider that the `data` pointer must point to a + /// thread safe type such as an `Arc`. + /// + /// The `vtable` customizes the behavior of a `Waker`. For each operation + /// on the `Waker`, the associated function in the `vtable` will be called. + /// + /// # Safety + /// + /// The behavior of the returned `Waker` is undefined if the contract defined + /// in [`RawWakerVTable`]'s documentation is not upheld. + /// + /// (Authors wishing to avoid unsafe code may implement the [`Wake`] trait instead, at the + /// cost of a required heap allocation.) + /// + /// [`Wake`]: ../../alloc/task/trait.Wake.html + #[inline] + #[must_use] + #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] + pub const unsafe fn new(data: *const (), vtable: &'static RawWakerVTable) -> Self { + Waker { waker: RawWaker { data, vtable } } + } + /// Creates a new `Waker` from [`RawWaker`]. /// /// # Safety @@ -770,6 +801,30 @@ impl LocalWaker { a_data == b_data && ptr::eq(a_vtable, b_vtable) } + /// Creates a new `LocalWaker` from the provided `data` pointer and `vtable`. + /// + /// The `data` pointer can be used to store arbitrary data as required + /// by the executor. This could be e.g. a type-erased pointer to an `Arc` + /// that is associated with the task. + /// The value of this pointer will get passed to all functions that are part + /// of the `vtable` as the first parameter. + /// + /// The `vtable` customizes the behavior of a `LocalWaker`. For each + /// operation on the `LocalWaker`, the associated function in the `vtable` + /// will be called. + /// + /// # Safety + /// + /// The behavior of the returned `Waker` is undefined if the contract defined + /// in [`RawWakerVTable`]'s documentation is not upheld. + /// + #[inline] + #[must_use] + #[unstable(feature = "local_waker", issue = "118959")] + pub const unsafe fn new(data: *const (), vtable: &'static RawWakerVTable) -> Self { + LocalWaker { waker: RawWaker { data, vtable } } + } + /// Creates a new `LocalWaker` from [`RawWaker`]. /// /// The behavior of the returned `LocalWaker` is undefined if the contract defined From 6f435cb07f647f41136af6c175dd6964dd7142b4 Mon Sep 17 00:00:00 2001 From: Jan Sommer <jan.sommer@dlr.de> Date: Mon, 21 Aug 2023 19:03:17 +0200 Subject: [PATCH 30/36] Port std library to RTEMS --- library/core/Cargo.toml | 2 + library/core/src/ffi/mod.rs | 2 +- library/panic_unwind/Cargo.toml | 7 + library/panic_unwind/src/lib.rs | 2 +- library/std/Cargo.toml | 2 + library/std/build.rs | 1 + library/std/src/os/mod.rs | 2 + library/std/src/os/rtems/fs.rs | 374 ++++++++++++++++++ library/std/src/os/rtems/mod.rs | 4 + library/std/src/os/rtems/raw.rs | 33 ++ library/std/src/os/unix/mod.rs | 2 + library/std/src/sys/pal/unix/args.rs | 1 + library/std/src/sys/pal/unix/env.rs | 11 + library/std/src/sys/pal/unix/fs.rs | 18 +- library/std/src/sys/pal/unix/mod.rs | 1 + library/std/src/sys/pal/unix/os.rs | 19 +- .../src/sys/pal/unix/process/process_unix.rs | 6 +- library/std/src/sys/personality/mod.rs | 2 +- library/unwind/Cargo.toml | 7 + library/unwind/src/lib.rs | 1 + src/bootstrap/src/core/sanity.rs | 1 + tests/ui/check-cfg/well-known-values.stderr | 4 +- 22 files changed, 488 insertions(+), 14 deletions(-) create mode 100644 library/std/src/os/rtems/fs.rs create mode 100644 library/std/src/os/rtems/mod.rs create mode 100644 library/std/src/os/rtems/raw.rs diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index cace4582b48..94f343d0670 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -43,6 +43,8 @@ check-cfg = [ 'cfg(bootstrap)', 'cfg(no_fp_fmt_parse)', 'cfg(stdarch_intel_sde)', + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', # core use #[path] imports to portable-simd `core_simd` crate # and to stdarch `core_arch` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index ec1f9052a15..dc107c5d22c 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -110,7 +110,7 @@ mod c_char_definition { all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), all(target_os = "l4re", target_arch = "x86_64"), all( - any(target_os = "freebsd", target_os = "openbsd"), + any(target_os = "freebsd", target_os = "openbsd", target_os = "rtems"), any( target_arch = "aarch64", target_arch = "arm", diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index f830808d196..6d1f9764efb 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -20,3 +20,10 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2", default-features = false } + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', +] diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 2d174f4b1a4..4552fb68d26 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -48,7 +48,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf")), + all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems"))), all(target_vendor = "fortanix", target_env = "sgx"), target_family = "wasm", ))] { diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 82cfd603a21..e20fe9feff1 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -146,4 +146,6 @@ check-cfg = [ # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg 'cfg(feature, values(any()))', + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', ] diff --git a/library/std/build.rs b/library/std/build.rs index 72254cafc85..ba1eece46f3 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -53,6 +53,7 @@ fn main() { || target_os == "uefi" || target_os == "teeos" || target_os == "zkvm" + || target_os == "rtems" // See src/bootstrap/src/core/build_steps/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 020a8b324f4..a2496baa63f 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -143,6 +143,8 @@ pub mod nto; pub mod openbsd; #[cfg(target_os = "redox")] pub mod redox; +#[cfg(target_os = "rtems")] +pub mod rtems; #[cfg(target_os = "solaris")] pub mod solaris; #[cfg(target_os = "solid_asp3")] diff --git a/library/std/src/os/rtems/fs.rs b/library/std/src/os/rtems/fs.rs new file mode 100644 index 00000000000..bec0d41e42d --- /dev/null +++ b/library/std/src/os/rtems/fs.rs @@ -0,0 +1,374 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_dev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ino()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + + /// Returns the file type and mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mode()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + + /// Returns the number of hard links to file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_nlink()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + + /// Returns the user ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_uid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + + /// Returns the group ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_gid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + + /// Returns the device ID that this file represents. Only relevant for special file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_rdev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + + /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes. + /// + /// The size of a symbolic link is the length of the pathname it contains, + /// without a terminating null byte. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_size()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + + /// Returns the last access time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + + /// Returns the last access time of the file, in nanoseconds since [`st_atime`]. + /// + /// [`st_atime`]: Self::st_atime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + + /// Returns the last modification time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + + /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`]. + /// + /// [`st_mtime`]: Self::st_mtime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + + /// Returns the last status change time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + + /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`]. + /// + /// [`st_ctime`]: Self::st_ctime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + + /// Returns the "preferred" block size for efficient filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blksize()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + + /// Returns the number of blocks allocated to the file, 512-byte units. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blocks()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + + fn st_atime_nsec(&self) -> i64 { + 0 + } + + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + + fn st_mtime_nsec(&self) -> i64 { + 0 + } + + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + + fn st_ctime_nsec(&self) -> i64 { + 0 + } + + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/rtems/mod.rs b/library/std/src/os/rtems/mod.rs new file mode 100644 index 00000000000..7275bfd1765 --- /dev/null +++ b/library/std/src/os/rtems/mod.rs @@ -0,0 +1,4 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] +pub mod fs; +pub(crate) mod raw; diff --git a/library/std/src/os/rtems/raw.rs b/library/std/src/os/rtems/raw.rs new file mode 100644 index 00000000000..113079cf4ab --- /dev/null +++ b/library/std/src/os/rtems/raw.rs @@ -0,0 +1,33 @@ +//! rtems raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = libc::pthread_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = libc::blkcnt_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = libc::blksize_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = libc::dev_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = libc::ino_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = libc::mode_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = libc::nlink_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = libc::off_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = libc::time_t; diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index c6581b9c4c8..7d2f0bd4efe 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -73,6 +73,8 @@ mod platform { pub use crate::os::openbsd::*; #[cfg(target_os = "redox")] pub use crate::os::redox::*; + #[cfg(target_os = "rtems")] + pub use crate::os::rtems::*; #[cfg(target_os = "solaris")] pub use crate::os::solaris::*; #[cfg(target_os = "vita")] diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 9a37e1a0346..a943e3a581a 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -112,6 +112,7 @@ impl DoubleEndedIterator for Args { target_os = "aix", target_os = "nto", target_os = "hurd", + target_os = "rtems", ))] mod imp { use crate::ffi::c_char; diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs index fb1f868644d..b2d399b8791 100644 --- a/library/std/src/sys/pal/unix/env.rs +++ b/library/std/src/sys/pal/unix/env.rs @@ -240,6 +240,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "rtems")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "rtems"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "vxworks")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 7fa147c9754..4ec577a0a01 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -478,6 +478,7 @@ impl FileAttr { target_os = "horizon", target_os = "vita", target_os = "hurd", + target_os = "rtems", )))] pub fn modified(&self) -> io::Result<SystemTime> { #[cfg(target_pointer_width = "32")] @@ -490,7 +491,12 @@ impl FileAttr { SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64) } - #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] + #[cfg(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "vita", + target_os = "rtems", + ))] pub fn modified(&self) -> io::Result<SystemTime> { SystemTime::new(self.stat.st_mtime as i64, 0) } @@ -506,6 +512,7 @@ impl FileAttr { target_os = "horizon", target_os = "vita", target_os = "hurd", + target_os = "rtems", )))] pub fn accessed(&self) -> io::Result<SystemTime> { #[cfg(target_pointer_width = "32")] @@ -518,7 +525,12 @@ impl FileAttr { SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64) } - #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] + #[cfg(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "vita", + target_os = "rtems" + ))] pub fn accessed(&self) -> io::Result<SystemTime> { SystemTime::new(self.stat.st_atime as i64, 0) } @@ -853,6 +865,7 @@ impl Drop for Dir { target_os = "fuchsia", target_os = "horizon", target_os = "vxworks", + target_os = "rtems", )))] { let fd = unsafe { libc::dirfd(self.0) }; @@ -970,6 +983,7 @@ impl DirEntry { target_os = "aix", target_os = "nto", target_os = "hurd", + target_os = "rtems", target_vendor = "apple", ))] pub fn ino(&self) -> u64 { diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index ba2f58f9c10..e8428eccb16 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -79,6 +79,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "l4re", target_os = "horizon", target_os = "vita", + target_os = "rtems", // The poll on Darwin doesn't set POLLNVAL for closed fds. target_vendor = "apple", )))] diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index a785b97ac8d..503f8915256 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { } extern "C" { - #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] + #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] #[cfg_attr( any( target_os = "linux", @@ -61,13 +61,14 @@ extern "C" { } /// Returns the platform-specific value of errno -#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] +#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] pub fn errno() -> i32 { unsafe { (*errno_location()) as i32 } } /// Sets the platform-specific value of errno -#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall! +// needed for readdir and syscall! +#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] #[allow(dead_code)] // but not all target cfgs actually end up using it pub fn set_errno(e: i32) { unsafe { *errno_location() = e as c_int } @@ -78,6 +79,16 @@ pub fn errno() -> i32 { unsafe { libc::errnoGet() } } +#[cfg(target_os = "rtems")] +pub fn errno() -> i32 { + extern "C" { + #[thread_local] + static _tls_errno: c_int; + } + + unsafe { _tls_errno as i32 } +} + #[cfg(target_os = "dragonfly")] pub fn errno() -> i32 { extern "C" { @@ -472,7 +483,7 @@ pub fn current_exe() -> io::Result<PathBuf> { } } -#[cfg(target_os = "redox")] +#[cfg(any(target_os = "redox", target_os = "rtems"))] pub fn current_exe() -> io::Result<PathBuf> { crate::fs::read_to_string("sys:exe").map(PathBuf::from) } diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index 9d091f033e0..4bb22f36709 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -1089,13 +1089,13 @@ fn signal_string(signal: i32) -> &'static str { libc::SIGURG => " (SIGURG)", #[cfg(not(target_os = "l4re"))] libc::SIGXCPU => " (SIGXCPU)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGXFSZ => " (SIGXFSZ)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGVTALRM => " (SIGVTALRM)", #[cfg(not(target_os = "l4re"))] libc::SIGPROF => " (SIGPROF)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGWINCH => " (SIGWINCH)", #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] libc::SIGIO => " (SIGIO)", diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 1a6ea1dafcb..68085d026c4 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index bbd1db8dfa5..590de31a678 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -34,3 +34,10 @@ llvm-libunwind = [] # If crt-static is enabled, static link to `libunwind.a` provided by system # If crt-static is disabled, dynamic link to `libunwind.so` provided by system system-llvm-libunwind = [] + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', +] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 250af912e07..26ed00bfbd5 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -22,6 +22,7 @@ cfg_if::cfg_if! { target_os = "l4re", target_os = "none", target_os = "espidf", + target_os = "rtems", ))] { // These "unix" family members do not have unwinder. } else if #[cfg(any( diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 60ec57d0d44..50502389b86 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -34,6 +34,7 @@ pub struct Finder { // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined + "armv7-rtems-eabihf", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 0530e1c34c9..144a67025b3 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -210,7 +210,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -294,7 +294,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration warning: 30 warnings emitted From 124454cda84b291c105712c44365a374a16f1119 Mon Sep 17 00:00:00 2001 From: Jan Sommer <jan.sommer@dlr.de> Date: Tue, 23 Jan 2024 17:57:12 +0100 Subject: [PATCH 31/36] rtems: Add spec file for arm_rtems6_eabihf --- compiler/rustc_target/src/spec/mod.rs | 2 ++ .../src/spec/targets/armv7_rtems_eabihf.rs | 35 +++++++++++++++++++ tests/assembly/targets/targets-elf.rs | 3 ++ 3 files changed, 40 insertions(+) create mode 100644 compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f992ddf8417..1c693a830d5 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1695,6 +1695,8 @@ supported_targets! { ("armv7r-none-eabihf", armv7r_none_eabihf), ("armv8r-none-eabihf", armv8r_none_eabihf), + ("armv7-rtems-eabihf", armv7_rtems_eabihf), + ("x86_64-pc-solaris", x86_64_pc_solaris), ("sparcv9-sun-solaris", sparcv9_sun_solaris), diff --git a/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs new file mode 100644 index 00000000000..1edecac095f --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs @@ -0,0 +1,35 @@ +use crate::spec::{cvs, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "armv7-unknown-none-eabihf".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Armv7 RTEMS (Requires RTEMS toolchain and kernel".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + + options: TargetOptions { + os: "rtems".into(), + families: cvs!["unix"], + abi: "eabihf".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), + linker: None, + relocation_model: RelocModel::Static, + panic_strategy: PanicStrategy::Abort, + features: "+thumb2,+neon,+vfp3".into(), + max_atomic_width: Some(64), + emit_debug_gdb_scripts: false, + // GCC defaults to 8 for arm-none here. + c_enum_min_bits: Some(8), + eh_frame_header: false, + no_default_libraries: false, + env: "newlib".into(), + ..Default::default() + }, + } +} diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index b0c5eec1fe4..c8610e03939 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -129,6 +129,9 @@ //@ revisions: armv7_linux_androideabi //@ [armv7_linux_androideabi] compile-flags: --target armv7-linux-androideabi //@ [armv7_linux_androideabi] needs-llvm-components: arm +//@ revisions: armv7_rtems_eabihf +//@ [armv7_rtems_eabihf] compile-flags: --target armv7-rtems-eabihf +//@ [armv7_rtems_eabihf] needs-llvm-components: arm //@ revisions: armv7_sony_vita_newlibeabihf //@ [armv7_sony_vita_newlibeabihf] compile-flags: --target armv7-sony-vita-newlibeabihf //@ [armv7_sony_vita_newlibeabihf] needs-llvm-components: arm From 6fd358e99d5cae717a19ce4e397f26a82d0cd9ce Mon Sep 17 00:00:00 2001 From: Jan Sommer <jan.sommer@dlr.de> Date: Wed, 26 Jun 2024 16:23:24 +0200 Subject: [PATCH 32/36] Add documentation for target armv7-rtems-eabihf --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../platform-support/armv7-rtems-eabihf.md | 52 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/doc/rustc/src/platform-support/armv7-rtems-eabihf.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index b3a74a7716b..3e199539694 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -40,6 +40,7 @@ - [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md) - [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md) - [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md) + - [armv7-rtems-eabihf](platform-support/armv7-rtems-eabihf.md) - [armv7-sony-vita-newlibeabihf](platform-support/armv7-sony-vita-newlibeabihf.md) - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md) - [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index a2641b22753..319dc9a7c08 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -280,6 +280,7 @@ target | std | host | notes `armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD [`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv6 NetBSD w/hard-float [`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? | | Armv6k Nintendo 3DS, Horizon (Requires devkitARM toolchain) +[`armv7-rtems-eabihf`](platform-support/armv7-rtems-eabihf.md) | ? | | RTEMS OS for ARM BSPs [`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ | | Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain) [`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat diff --git a/src/doc/rustc/src/platform-support/armv7-rtems-eabihf.md b/src/doc/rustc/src/platform-support/armv7-rtems-eabihf.md new file mode 100644 index 00000000000..2791c21ee45 --- /dev/null +++ b/src/doc/rustc/src/platform-support/armv7-rtems-eabihf.md @@ -0,0 +1,52 @@ +# `armv7-rtems-eabihf` + +**Tier: 3** + +ARM targets for the [RTEMS realtime operating system](https://www.rtems.org) using the RTEMS gcc cross-compiler for linking against the libraries of a specified Board Support Package (BSP). + +## Target maintainers + +- [@thesummer](https://github.com/thesummer) + +## Requirements + +The target does not support host tools. Only cross-compilation is possible. +The cross-compiler toolchain can be obtained by following the installation instructions +of the [RTEMS Documentation](https://docs.rtems.org/branches/master/user/index.html). Additionally to the cross-compiler also a compiled BSP +for a board fitting the architecture needs to be available on the host. +Currently tested has been the BSP `xilinx_zynq_a9_qemu` of RTEMS 6. + +`std` support is available, but not yet fully tested. Do NOT use in flight software! + +The target follows the EABI calling convention for `extern "C"`. + +The resulting binaries are in ELF format. + +## Building the target + +The target can be built by the standard compiler of Rust. + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy of `core` by using +`build-std` or similar. + +In order to build an RTEMS executable it is also necessary to have a basic RTEMS configuration (in C) compiled to link against as this configures the operating system. +An example can be found at this [`rtems-sys`](https://github.com/thesummer/rtems-sys) crate which could be added as an dependency to your application. + +## Testing + +The resulting binaries run fine on an emulated target (possibly also on a real Zedboard or similar). +For example, on qemu the following command can execute the binary: +```sh +qemu-system-arm -no-reboot -serial null -serial mon:stdio -net none -nographic -M xilinx-zynq-a9 -m 512M -kernel <binary file> +``` + +While basic execution of the unit test harness seems to work. However, running the Rust testsuite on the (emulated) hardware has not yet been tested. + +## Cross-compilation toolchains and C code + +Compatible C-code can be built with the RTEMS cross-compiler toolchain `arm-rtems6-gcc`. +For more information how to build the toolchain, RTEMS itself and RTEMS applications please have a look at the [RTEMS Documentation](https://docs.rtems.org/branches/master/user/index.html). From 42d2d7894ced2bb4b629ece4ff861e3f5afa8dbc Mon Sep 17 00:00:00 2001 From: Michael Goulet <michael@errs.io> Date: Thu, 29 Aug 2024 12:53:30 -0400 Subject: [PATCH 33/36] Make supertrait and implied predicates queries defaulted --- compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 4 ++-- compiler/rustc_metadata/src/rmeta/encoder.rs | 8 ++++---- compiler/rustc_metadata/src/rmeta/mod.rs | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 1264510a831..53da07aeaa6 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -247,8 +247,8 @@ provide! { tcx, def_id, other, cdata, explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } - explicit_super_predicates_of => { table } - explicit_implied_predicates_of => { table } + explicit_super_predicates_of => { table_defaulted_array } + explicit_implied_predicates_of => { table_defaulted_array } type_of => { table } type_alias_is_lazy => { table_direct } variances_of => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 88256c4db04..c55583b39a6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1443,9 +1443,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::Trait = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); - record_array!(self.tables.explicit_super_predicates_of[def_id] <- + record_defaulted_array!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id).skip_binder()); - record_array!(self.tables.explicit_implied_predicates_of[def_id] <- + record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id).skip_binder()); let module_children = self.tcx.module_children_local(local_id); @@ -1454,9 +1454,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::TraitAlias = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); - record_array!(self.tables.explicit_super_predicates_of[def_id] <- + record_defaulted_array!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id).skip_binder()); - record_array!(self.tables.explicit_implied_predicates_of[def_id] <- + record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id).skip_binder()); } if let DefKind::Trait | DefKind::Impl { .. } = def_kind { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a84923130c3..8180a507a51 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -390,6 +390,8 @@ define_tables! { explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, explicit_item_super_predicates: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, + explicit_super_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, + explicit_implied_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, inherent_impls: Table<DefIndex, LazyArray<DefIndex>>, associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>, associated_type_for_effects: Table<DefIndex, Option<LazyValue<DefId>>>, @@ -419,10 +421,6 @@ define_tables! { lookup_deprecation_entry: Table<DefIndex, LazyValue<attr::Deprecation>>, explicit_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>, generics_of: Table<DefIndex, LazyValue<ty::Generics>>, - explicit_super_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, - // As an optimization, we only store this for trait aliases, - // since it's identical to explicit_super_predicates_of for traits. - explicit_implied_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, type_of: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, Ty<'static>>>>, variances_of: Table<DefIndex, LazyArray<ty::Variance>>, fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::PolyFnSig<'static>>>>, From c753d2dbf93a66c521dda0caa6d4c7d70bd4cc35 Mon Sep 17 00:00:00 2001 From: onur-ozkan <work@onurozkan.dev> Date: Tue, 3 Sep 2024 08:21:57 +0300 Subject: [PATCH 34/36] remove deprecated option `rust.split-debuginfo` Signed-off-by: onur-ozkan <work@onurozkan.dev> --- src/bootstrap/src/core/config/config.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index fba80b92f45..466473fa5ec 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -268,7 +268,6 @@ pub struct Config { pub rust_debuginfo_level_std: DebuginfoLevel, pub rust_debuginfo_level_tools: DebuginfoLevel, pub rust_debuginfo_level_tests: DebuginfoLevel, - pub rust_split_debuginfo_for_build_triple: Option<SplitDebuginfo>, // FIXME: Deprecated field. Remove in Q3'24. pub rust_rpath: bool, pub rust_strip: bool, pub rust_frame_pointers: bool, @@ -1099,7 +1098,6 @@ define_config! { debuginfo_level_std: Option<DebuginfoLevel> = "debuginfo-level-std", debuginfo_level_tools: Option<DebuginfoLevel> = "debuginfo-level-tools", debuginfo_level_tests: Option<DebuginfoLevel> = "debuginfo-level-tests", - split_debuginfo: Option<String> = "split-debuginfo", backtrace: Option<bool> = "backtrace", incremental: Option<bool> = "incremental", parallel_compiler: Option<bool> = "parallel-compiler", @@ -1636,7 +1634,6 @@ impl Config { debuginfo_level_std: debuginfo_level_std_toml, debuginfo_level_tools: debuginfo_level_tools_toml, debuginfo_level_tests: debuginfo_level_tests_toml, - split_debuginfo, backtrace, incremental, parallel_compiler, @@ -1695,18 +1692,6 @@ impl Config { debuginfo_level_tests = debuginfo_level_tests_toml; lld_enabled = lld_enabled_toml; - config.rust_split_debuginfo_for_build_triple = split_debuginfo - .as_deref() - .map(SplitDebuginfo::from_str) - .map(|v| v.expect("invalid value for rust.split-debuginfo")); - - if config.rust_split_debuginfo_for_build_triple.is_some() { - println!( - "WARNING: specifying `rust.split-debuginfo` is deprecated, use `target.{}.split-debuginfo` instead", - config.build - ); - } - optimize = optimize_toml; omit_git_hash = omit_git_hash_toml; config.rust_new_symbol_mangling = new_symbol_mangling; @@ -2504,9 +2489,6 @@ impl Config { self.target_config .get(&target) .and_then(|t| t.split_debuginfo) - .or_else(|| { - if self.build == target { self.rust_split_debuginfo_for_build_triple } else { None } - }) .unwrap_or_else(|| SplitDebuginfo::default_for_platform(target)) } @@ -2927,7 +2909,6 @@ fn check_incompatible_options_for_ci_rustc( debuginfo_level_std: _, debuginfo_level_tools: _, debuginfo_level_tests: _, - split_debuginfo: _, backtrace: _, parallel_compiler: _, musl_root: _, From c06ed92625e1c1b723ea369114b994c4943a271d Mon Sep 17 00:00:00 2001 From: onur-ozkan <work@onurozkan.dev> Date: Wed, 4 Sep 2024 13:49:52 +0300 Subject: [PATCH 35/36] add change entry for `rust.split-debuginfo` removal Signed-off-by: onur-ozkan <work@onurozkan.dev> --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 3ab14aade9f..3fec2d953d9 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -240,4 +240,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New option `build.cargo-clippy` added for supporting the use of custom/external clippy.", }, + ChangeInfo { + change_id: 129925, + severity: ChangeSeverity::Warning, + summary: "Removed `rust.split-debuginfo` as it was deprecated long time ago.", + }, ]; From 0c45d3bb705e12251eb1914816373792d081ef35 Mon Sep 17 00:00:00 2001 From: Kalle Wachsmuth <kalle.wachsmuth@gmail.com> Date: Mon, 2 Sep 2024 23:51:22 +0200 Subject: [PATCH 36/36] update docs of missing_abi lint --- compiler/rustc_lint_defs/src/builtin.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 7063f488209..25d33126754 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3706,7 +3706,7 @@ declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]); declare_lint! { /// The `missing_abi` lint detects cases where the ABI is omitted from - /// extern declarations. + /// `extern` declarations. /// /// ### Example /// @@ -3720,10 +3720,12 @@ declare_lint! { /// /// ### Explanation /// - /// Historically, Rust implicitly selected C as the ABI for extern - /// declarations. We expect to add new ABIs, like `C-unwind`, in the future, - /// though this has not yet happened, and especially with their addition - /// seeing the ABI easily will make code review easier. + /// For historic reasons, Rust implicitly selects `C` as the default ABI for + /// `extern` declarations. [Other ABIs] like `C-unwind` and `system` have + /// been added since then, and especially with their addition seeing the ABI + /// easily makes code review easier. + /// + /// [Other ABIs]: https://doc.rust-lang.org/reference/items/external-blocks.html#abi pub MISSING_ABI, Allow, "No declared ABI for extern declaration"