From 8a0a4df0c6725be431157ce84ffd7595c4bd7780 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 11 Feb 2025 09:37:59 +0530 Subject: Add docs to bootstrap:src:utils:cache --- src/bootstrap/src/utils/cache.rs | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'src') diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs index 29342cc5a2c..650316eebd5 100644 --- a/src/bootstrap/src/utils/cache.rs +++ b/src/bootstrap/src/utils/cache.rs @@ -1,3 +1,17 @@ +//! This module helps you efficiently store and retrieve values using interning. +//! +//! Interning is a neat trick that keeps only one copy of identical values, saving memory +//! and making comparisons super fast. Here, we provide the `Interned` struct and the `Internable` trait +//! to make interning easy for different data types. +//! +//! The `Interner` struct handles caching for common types like `String`, `PathBuf`, and `Vec`, +//! while the `Cache` struct acts as a write-once storage for linking computation steps with their results. +//! +//! # Thread Safety +//! +//! We use `Mutex` to make sure interning and retrieval are thread-safe. But keep in mind—once a value is +//! interned, it sticks around for the entire lifetime of the program. + use std::any::{Any, TypeId}; use std::borrow::Borrow; use std::cell::RefCell; @@ -12,6 +26,9 @@ use std::{fmt, mem}; use crate::core::builder::Step; +/// Represents an interned value of type `T`, allowing for efficient comparisons and retrieval. +/// +/// This struct stores a unique index referencing the interned value within an internal cache. pub struct Interned(usize, PhantomData<*const T>); impl Default for Interned { @@ -111,6 +128,10 @@ impl Ord for Interned { } } +/// A structure for managing the interning of values of type `T`. +/// +/// `TyIntern` maintains a mapping between values and their interned representations, +/// ensuring that duplicate values are not stored multiple times. struct TyIntern { items: Vec, set: HashMap>, @@ -123,6 +144,9 @@ impl Default for TyIntern { } impl TyIntern { + /// Interns a borrowed value, ensuring it is stored uniquely. + /// + /// If the value has been previously interned, the same `Interned` instance is returned. fn intern_borrow(&mut self, item: &B) -> Interned where B: Eq + Hash + ToOwned + ?Sized, @@ -138,6 +162,9 @@ impl TyIntern { interned } + /// Interns an owned value, storing it uniquely. + /// + /// If the value has been previously interned, the existing `Interned` is returned. fn intern(&mut self, item: T) -> Interned { if let Some(i) = self.set.get(&item) { return *i; @@ -148,11 +175,16 @@ impl TyIntern { interned } + /// Retrieves a reference to the interned value associated with the given `Interned` instance. fn get(&self, i: Interned) -> &T { &self.items[i.0] } } +/// A global interner for managing interned values of common types. +/// +/// This structure maintains caches for `String`, `PathBuf`, and `Vec`, ensuring efficient storage +/// and retrieval of frequently used values. #[derive(Default)] pub struct Interner { strs: Mutex>, @@ -160,6 +192,10 @@ pub struct Interner { lists: Mutex>>, } +/// Defines the behavior required for a type to be internable. +/// +/// Types implementing this trait must provide access to a static cache and define an `intern` method +/// that ensures values are stored uniquely. trait Internable: Clone + Eq + Hash + 'static { fn intern_cache() -> &'static Mutex>; @@ -187,11 +223,15 @@ impl Internable for Vec { } impl Interner { + /// Interns a string reference, ensuring it is stored uniquely. + /// + /// If the string has been previously interned, the same `Interned` instance is returned. pub fn intern_str(&self, s: &str) -> Interned { self.strs.lock().unwrap().intern_borrow(s) } } +/// A global instance of `Interner` that caches common interned values. pub static INTERNER: LazyLock = LazyLock::new(Interner::default); /// This is essentially a `HashMap` which allows storing any type in its input and @@ -209,10 +249,12 @@ pub struct Cache( ); impl Cache { + /// Creates a new empty cache. pub fn new() -> Cache { Cache(RefCell::new(HashMap::new())) } + /// Stores the result of a computation step in the cache. pub fn put(&self, step: S, value: S::Output) { let mut cache = self.0.borrow_mut(); let type_id = TypeId::of::(); @@ -225,6 +267,7 @@ impl Cache { stepcache.insert(step, value); } + /// Retrieves a cached result for the given step, if available. pub fn get(&self, step: &S) -> Option { let mut cache = self.0.borrow_mut(); let type_id = TypeId::of::(); -- cgit 1.4.1-3-g733a5 From 24150eb3c073e8f00ebd6b7d02ffac7ddd0f398e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 11 Feb 2025 10:33:05 +0530 Subject: add unit test to bootstrap:util:cache:tests --- src/bootstrap/src/utils/cache.rs | 3 ++ src/bootstrap/src/utils/cache/tests.rs | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/bootstrap/src/utils/cache/tests.rs (limited to 'src') diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs index 650316eebd5..1c8cc4025df 100644 --- a/src/bootstrap/src/utils/cache.rs +++ b/src/bootstrap/src/utils/cache.rs @@ -298,3 +298,6 @@ impl Cache { self.0.borrow().contains_key(&TypeId::of::()) } } + +#[cfg(test)] +mod tests; diff --git a/src/bootstrap/src/utils/cache/tests.rs b/src/bootstrap/src/utils/cache/tests.rs new file mode 100644 index 00000000000..28f5563a589 --- /dev/null +++ b/src/bootstrap/src/utils/cache/tests.rs @@ -0,0 +1,52 @@ +use std::path::PathBuf; + +use crate::utils::cache::{INTERNER, Internable, TyIntern}; + +#[test] +fn test_string_interning() { + let s1 = INTERNER.intern_str("Hello"); + let s2 = INTERNER.intern_str("Hello"); + let s3 = INTERNER.intern_str("world"); + + assert_eq!(s1, s2, "Same strings should be interned to the same instance"); + assert_ne!(s1, s3, "Different strings should have different interned values"); +} + +#[test] +fn test_path_interning() { + let p1 = PathBuf::from("/tmp/file").intern(); + let p2 = PathBuf::from("/tmp/file").intern(); + let p3 = PathBuf::from("/tmp/other").intern(); + + assert_eq!(p1, p2); + assert_ne!(p1, p3); +} + +#[test] +fn test_vec_interning() { + let v1 = vec!["a".to_string(), "b".to_string()].intern(); + let v2 = vec!["a".to_string(), "b".to_string()].intern(); + let v3 = vec!["c".to_string()].intern(); + + assert_eq!(v1, v2); + assert_ne!(v1, v3); +} + +#[test] +fn test_interned_equality() { + let s1 = INTERNER.intern_str("test"); + let s2 = INTERNER.intern_str("test"); + + assert_eq!(s1, s2); + assert_eq!(s1, "test"); +} + +#[test] +fn test_ty_intern_intern_borrow() { + let mut interner = TyIntern::default(); + let s1 = interner.intern_borrow("borrowed"); + let s2 = interner.intern("borrowed".to_string()); + + assert_eq!(s1, s2); + assert_eq!(interner.get(s1), "borrowed"); +} -- cgit 1.4.1-3-g733a5 From 7972aa2394583e3aa4cfe4d7c6eab05c3be56e94 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 11 Feb 2025 15:47:29 +0100 Subject: dev-guide: Link to t-lang procedures for new features --- src/doc/rustc-dev-guide/src/implementing_new_features.md | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing_new_features.md index d87afeaedce..5b67ccd7f4c 100644 --- a/src/doc/rustc-dev-guide/src/implementing_new_features.md +++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md @@ -9,7 +9,11 @@ smoothly. **NOTE: this section is for *language* features, not *library* features, which use [a different process].** +See also [the Rust Language Design Team's procedures][lang-propose] for +proposing changes to the language. + [a different process]: ./stability.md +[lang-propose]: https://lang-team.rust-lang.org/how_to/propose.html ## The @rfcbot FCP process -- cgit 1.4.1-3-g733a5 From 8a70219a38487a59de2449e920302d900506118d Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Wed, 12 Feb 2025 09:16:47 +0000 Subject: use cc archiver as default in `cc2ar` We should remove entire `cc2ar` but `cc` doesn't seem to cover all the conditions that `cc2ar` handles. For now, I replaced the `else` logic only, which is a bit hacky and unstable. Signed-off-by: onur-ozkan --- src/bootstrap/src/utils/cc_detect.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index f6afd50afce..45797c1276c 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -29,11 +29,8 @@ use crate::core::config::TargetSelection; use crate::utils::exec::{BootstrapCommand, command}; use crate::{Build, CLang, GitRepo}; -// The `cc` crate doesn't provide a way to obtain a path to the detected archiver, -// so use some simplified logic here. First we respect the environment variable `AR`, then -// try to infer the archiver path from the C compiler path. -// In the future this logic should be replaced by calling into the `cc` crate. -fn cc2ar(cc: &Path, target: TargetSelection) -> Option { +/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate. +fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option { if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) { Some(PathBuf::from(ar)) } else if let Some(ar) = env::var_os("AR") { @@ -57,16 +54,7 @@ fn cc2ar(cc: &Path, target: TargetSelection) -> Option { } else if target.contains("android") || target.contains("-wasi") { Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar"))) } else { - let parent = cc.parent().unwrap(); - let file = cc.file_name().unwrap().to_str().unwrap(); - for suffix in &["gcc", "cc", "clang"] { - if let Some(idx) = file.rfind(suffix) { - let mut file = file[..idx].to_owned(); - file.push_str("ar"); - return Some(parent.join(&file)); - } - } - Some(parent.join(file)) + Some(default_ar) } } @@ -138,7 +126,7 @@ pub fn find_target(build: &Build, target: TargetSelection) { let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) { ar } else { - cc2ar(compiler.path(), target) + cc2ar(compiler.path(), target, PathBuf::from(cfg.get_archiver().get_program())) }; build.cc.borrow_mut().insert(target, compiler.clone()); -- cgit 1.4.1-3-g733a5 From d99d8c249d3c965ab8a006dfe10a46214395682d Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sun, 9 Feb 2025 18:24:52 +0000 Subject: Nuke `Buffer` abstraction from `librustdoc` 💣 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/librustdoc/html/format.rs | 123 +----- src/librustdoc/html/highlight.rs | 80 ++-- src/librustdoc/html/highlight/tests.rs | 21 +- src/librustdoc/html/layout.rs | 25 +- src/librustdoc/html/markdown.rs | 5 +- src/librustdoc/html/render/context.rs | 67 ++-- src/librustdoc/html/render/mod.rs | 366 +++++++++-------- src/librustdoc/html/render/print_item.rs | 604 +++++++++++++++++------------ src/librustdoc/html/render/sidebar.rs | 3 +- src/librustdoc/html/render/tests.rs | 6 +- src/librustdoc/html/render/write_shared.rs | 39 +- src/librustdoc/html/sources.rs | 18 +- src/librustdoc/lib.rs | 1 + 13 files changed, 710 insertions(+), 648 deletions(-) (limited to 'src') diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index b6a73602a32..086a85aa616 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -37,113 +37,8 @@ use crate::html::render::Context; use crate::joined::Joined as _; use crate::passes::collect_intra_doc_links::UrlFragment; -pub(crate) trait Print { - fn print(self, buffer: &mut Buffer); -} - -impl Print for F -where - F: FnOnce(&mut Buffer), -{ - fn print(self, buffer: &mut Buffer) { - (self)(buffer) - } -} - -impl Print for String { - fn print(self, buffer: &mut Buffer) { - buffer.write_str(&self); - } -} - -impl Print for &'_ str { - fn print(self, buffer: &mut Buffer) { - buffer.write_str(self); - } -} - -#[derive(Debug, Clone)] -pub(crate) struct Buffer { - for_html: bool, - buffer: String, -} - -impl core::fmt::Write for Buffer { - #[inline] - fn write_str(&mut self, s: &str) -> fmt::Result { - self.buffer.write_str(s) - } - - #[inline] - fn write_char(&mut self, c: char) -> fmt::Result { - self.buffer.write_char(c) - } - - #[inline] - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { - self.buffer.write_fmt(args) - } -} - -impl Buffer { - pub(crate) fn empty_from(v: &Buffer) -> Buffer { - Buffer { for_html: v.for_html, buffer: String::new() } - } - - pub(crate) fn html() -> Buffer { - Buffer { for_html: true, buffer: String::new() } - } - - pub(crate) fn new() -> Buffer { - Buffer { for_html: false, buffer: String::new() } - } - - pub(crate) fn is_empty(&self) -> bool { - self.buffer.is_empty() - } - - pub(crate) fn into_inner(self) -> String { - self.buffer - } - - pub(crate) fn push(&mut self, c: char) { - self.buffer.push(c); - } - - pub(crate) fn push_str(&mut self, s: &str) { - self.buffer.push_str(s); - } - - pub(crate) fn push_buffer(&mut self, other: Buffer) { - self.buffer.push_str(&other.buffer); - } - - // Intended for consumption by write! and writeln! (std::fmt) but without - // the fmt::Result return type imposed by fmt::Write (and avoiding the trait - // import). - pub(crate) fn write_str(&mut self, s: &str) { - self.buffer.push_str(s); - } - - // Intended for consumption by write! and writeln! (std::fmt) but without - // the fmt::Result return type imposed by fmt::Write (and avoiding the trait - // import). - pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) { - self.buffer.write_fmt(v).unwrap(); - } - - pub(crate) fn to_display(mut self, t: T) -> String { - t.print(&mut self); - self.into_inner() - } - - pub(crate) fn reserve(&mut self, additional: usize) { - self.buffer.reserve(additional) - } - - pub(crate) fn len(&self) -> usize { - self.buffer.len() - } +pub(crate) fn write_str(s: &mut String, f: fmt::Arguments<'_>) { + s.write_fmt(f).unwrap(); } pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( @@ -772,7 +667,7 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option, cx: &Cont else { return String::new(); }; - let mut buf = Buffer::new(); + let mut buf = String::new(); let fqp = if *shortty == ItemType::Primitive { // primitives are documented in a crate, but not actually part of it &fqp[fqp.len() - 1..] @@ -780,19 +675,19 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option, cx: &Cont fqp }; if let &Some(UrlFragment::Item(id)) = fragment { - write!(buf, "{} ", cx.tcx().def_descr(id)); + write_str(&mut buf, format_args!("{} ", cx.tcx().def_descr(id))); for component in fqp { - write!(buf, "{component}::"); + write_str(&mut buf, format_args!("{component}::")); } - write!(buf, "{}", cx.tcx().item_name(id)); + write_str(&mut buf, format_args!("{}", cx.tcx().item_name(id))); } else if !fqp.is_empty() { let mut fqp_it = fqp.iter(); - write!(buf, "{shortty} {}", fqp_it.next().unwrap()); + write_str(&mut buf, format_args!("{shortty} {}", fqp_it.next().unwrap())); for component in fqp_it { - write!(buf, "::{component}"); + write_str(&mut buf, format_args!("::{component}")); } } - buf.into_inner() + buf } /// Used to render a [`clean::Path`]. diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 15bf968e0fc..ed4b97d3625 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -14,7 +14,7 @@ use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, DUMMY_SP, Span}; -use super::format::{self, Buffer}; +use super::format::{self, write_str}; use crate::clean::PrimitiveType; use crate::html::escape::EscapeBodyText; use crate::html::render::{Context, LinkFromSrc}; @@ -48,7 +48,7 @@ pub(crate) enum Tooltip { /// Highlights `src` as an inline example, returning the HTML output. pub(crate) fn render_example_with_highlighting( src: &str, - out: &mut Buffer, + out: &mut String, tooltip: Tooltip, playground_button: Option<&str>, extra_classes: &[String], @@ -59,61 +59,69 @@ pub(crate) fn render_example_with_highlighting( } fn write_header( - out: &mut Buffer, + out: &mut String, class: &str, - extra_content: Option, + extra_content: Option<&str>, tooltip: Tooltip, extra_classes: &[String], ) { - write!( + write_str( out, - "
", - match tooltip { - Tooltip::Ignore => " ignore", - Tooltip::CompileFail => " compile_fail", - Tooltip::ShouldPanic => " should_panic", - Tooltip::Edition(_) => " edition", - Tooltip::None => "", - }, + format_args!( + "
", + match tooltip { + Tooltip::Ignore => " ignore", + Tooltip::CompileFail => " compile_fail", + Tooltip::ShouldPanic => " should_panic", + Tooltip::Edition(_) => " edition", + Tooltip::None => "", + } + ), ); if tooltip != Tooltip::None { let edition_code; - write!( + write_str( out, - "", - match tooltip { - Tooltip::Ignore => "This example is not tested", - Tooltip::CompileFail => "This example deliberately fails to compile", - Tooltip::ShouldPanic => "This example panics", - Tooltip::Edition(edition) => { - edition_code = format!("This example runs with edition {edition}"); - &edition_code + format_args!( + "", + match tooltip { + Tooltip::Ignore => "This example is not tested", + Tooltip::CompileFail => "This example deliberately fails to compile", + Tooltip::ShouldPanic => "This example panics", + Tooltip::Edition(edition) => { + edition_code = format!("This example runs with edition {edition}"); + &edition_code + } + Tooltip::None => unreachable!(), } - Tooltip::None => unreachable!(), - }, + ), ); } if let Some(extra) = extra_content { - out.push_buffer(extra); + out.push_str(&extra); } if class.is_empty() { - write!( + write_str( out, - "
",
-            if extra_classes.is_empty() { "" } else { " " },
-            extra_classes.join(" "),
+            format_args!(
+                "
",
+                if extra_classes.is_empty() { "" } else { " " },
+                extra_classes.join(" ")
+            ),
         );
     } else {
-        write!(
+        write_str(
             out,
-            "
",
-            if extra_classes.is_empty() { "" } else { " " },
-            extra_classes.join(" "),
+            format_args!(
+                "
",
+                if extra_classes.is_empty() { "" } else { " " },
+                extra_classes.join(" ")
+            ),
         );
     }
-    write!(out, "");
+    write_str(out, format_args!(""));
 }
 
 /// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None`
@@ -398,8 +406,8 @@ pub(super) fn write_code(
     });
 }
 
-fn write_footer(out: &mut Buffer, playground_button: Option<&str>) {
-    writeln!(out, "
{}
", playground_button.unwrap_or_default()); +fn write_footer(out: &mut String, playground_button: Option<&str>) { + write_str(out, format_args_nl!("{}
", playground_button.unwrap_or_default())); } /// How a span of text is classified. Mostly corresponds to token kinds. diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index 8f39130bb83..2603e887bea 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -3,7 +3,6 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_span::create_default_session_globals_then; use super::{DecorationInfo, write_code}; -use crate::html::format::Buffer; const STYLE: &str = r#"