diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 3875f61efafdf..1d47838e26a14 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -2921,7 +2921,7 @@ impl<'a, K, V> Cursor<'a, K, V> { /// Returns a reference to the key and value of the next element without /// moving the cursor. /// - /// If the cursor is at the end of the map then `None` is returned + /// If the cursor is at the end of the map then `None` is returned. #[unstable(feature = "btree_cursors", issue = "107540")] pub fn peek_next(&self) -> Option<(&'a K, &'a V)> { self.clone().next() @@ -2963,7 +2963,7 @@ impl<'a, K, V, A> CursorMut<'a, K, V, A> { /// Returns a reference to the key and value of the next element without /// moving the cursor. /// - /// If the cursor is at the end of the map then `None` is returned + /// If the cursor is at the end of the map then `None` is returned. #[unstable(feature = "btree_cursors", issue = "107540")] pub fn peek_next(&mut self) -> Option<(&K, &mut V)> { let (k, v) = self.inner.peek_next()?; @@ -3061,7 +3061,7 @@ impl<'a, K, V, A> CursorMutKey<'a, K, V, A> { /// Returns a reference to the key and value of the next element without /// moving the cursor. /// - /// If the cursor is at the end of the map then `None` is returned + /// If the cursor is at the end of the map then `None` is returned. #[unstable(feature = "btree_cursors", issue = "107540")] pub fn peek_next(&mut self) -> Option<(&mut K, &mut V)> { let current = self.current.as_mut()?; diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 2ed64a40495ef..596022d62a52a 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -210,55 +210,60 @@ impl Cursor where T: AsRef<[u8]>, { - /// Returns the remaining slice. + /// Splits the underlying slice at the cursor position and returns them. /// /// # Examples /// /// ``` - /// #![feature(cursor_remaining)] + /// #![feature(cursor_split)] /// use std::io::Cursor; /// /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); /// - /// assert_eq!(buff.remaining_slice(), &[1, 2, 3, 4, 5]); + /// assert_eq!(buff.split(), ([].as_slice(), [1, 2, 3, 4, 5].as_slice())); /// /// buff.set_position(2); - /// assert_eq!(buff.remaining_slice(), &[3, 4, 5]); - /// - /// buff.set_position(4); - /// assert_eq!(buff.remaining_slice(), &[5]); + /// assert_eq!(buff.split(), ([1, 2].as_slice(), [3, 4, 5].as_slice())); /// /// buff.set_position(6); - /// assert_eq!(buff.remaining_slice(), &[]); + /// assert_eq!(buff.split(), ([1, 2, 3, 4, 5].as_slice(), [].as_slice())); /// ``` - #[unstable(feature = "cursor_remaining", issue = "86369")] - pub fn remaining_slice(&self) -> &[u8] { - let len = self.pos.min(self.inner.as_ref().len() as u64); - &self.inner.as_ref()[(len as usize)..] + #[unstable(feature = "cursor_split", issue = "86369")] + pub fn split(&self) -> (&[u8], &[u8]) { + let slice = self.inner.as_ref(); + let pos = self.pos.min(slice.len() as u64); + slice.split_at(pos as usize) } +} - /// Returns `true` if the remaining slice is empty. +impl Cursor +where + T: AsMut<[u8]>, +{ + /// Splits the underlying slice at the cursor position and returns them + /// mutably. /// /// # Examples /// /// ``` - /// #![feature(cursor_remaining)] + /// #![feature(cursor_split)] /// use std::io::Cursor; /// /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); /// - /// buff.set_position(2); - /// assert!(!buff.is_empty()); + /// assert_eq!(buff.split_mut(), ([].as_mut_slice(), [1, 2, 3, 4, 5].as_mut_slice())); /// - /// buff.set_position(5); - /// assert!(buff.is_empty()); + /// buff.set_position(2); + /// assert_eq!(buff.split_mut(), ([1, 2].as_mut_slice(), [3, 4, 5].as_mut_slice())); /// - /// buff.set_position(10); - /// assert!(buff.is_empty()); + /// buff.set_position(6); + /// assert_eq!(buff.split_mut(), ([1, 2, 3, 4, 5].as_mut_slice(), [].as_mut_slice())); /// ``` - #[unstable(feature = "cursor_remaining", issue = "86369")] - pub fn is_empty(&self) -> bool { - self.pos >= self.inner.as_ref().len() as u64 + #[unstable(feature = "cursor_split", issue = "86369")] + pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) { + let slice = self.inner.as_mut(); + let pos = self.pos.min(slice.len() as u64); + slice.split_at_mut(pos as usize) } } @@ -320,7 +325,7 @@ where T: AsRef<[u8]>, { fn read(&mut self, buf: &mut [u8]) -> io::Result { - let n = Read::read(&mut self.remaining_slice(), buf)?; + let n = Read::read(&mut Cursor::split(self).1, buf)?; self.pos += n as u64; Ok(n) } @@ -328,7 +333,7 @@ where fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let prev_written = cursor.written(); - Read::read_buf(&mut self.remaining_slice(), cursor.reborrow())?; + Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?; self.pos += (cursor.written() - prev_written) as u64; @@ -352,7 +357,7 @@ where } fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let result = Read::read_exact(&mut self.remaining_slice(), buf); + let result = Read::read_exact(&mut Cursor::split(self).1, buf); match result { Ok(_) => self.pos += buf.len() as u64, @@ -366,14 +371,14 @@ where fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let prev_written = cursor.written(); - let result = Read::read_buf_exact(&mut self.remaining_slice(), cursor.reborrow()); + let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow()); self.pos += (cursor.written() - prev_written) as u64; result } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - let content = self.remaining_slice(); + let content = Cursor::split(self).1; let len = content.len(); buf.try_reserve(len)?; buf.extend_from_slice(content); @@ -384,7 +389,7 @@ where fn read_to_string(&mut self, buf: &mut String) -> io::Result { let content = - crate::str::from_utf8(self.remaining_slice()).map_err(|_| io::Error::INVALID_UTF8)?; + crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?; let len = content.len(); buf.try_reserve(len)?; buf.push_str(content); @@ -400,7 +405,7 @@ where T: AsRef<[u8]>, { fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(self.remaining_slice()) + Ok(Cursor::split(self).1) } fn consume(&mut self, amt: usize) { self.pos += amt as u64; diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index f613626bca843..e7472dd821572 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -675,13 +675,13 @@ fn cursor_read_exact_eof() { let mut r = slice.clone(); assert!(r.read_exact(&mut [0; 10]).is_err()); - assert!(r.is_empty()); + assert!(Cursor::split(&r).1.is_empty()); let mut r = slice; let buf = &mut [0; 10]; let mut buf = BorrowedBuf::from(buf.as_mut_slice()); assert!(r.read_buf_exact(buf.unfilled()).is_err()); - assert!(r.is_empty()); + assert!(Cursor::split(&r).1.is_empty()); assert_eq!(buf.filled(), b"123456"); } diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 3e0bb2884cd7d..236af2361dafd 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -1,7 +1,7 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. use crate::core::build_steps::compile::{ - add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, + add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; use crate::core::build_steps::tool::{prepare_tool_cargo, SourceType}; use crate::core::builder::{ @@ -47,7 +47,7 @@ impl Step for Std { } fn make_run(run: RunConfig<'_>) { - let crates = run.make_run_crates(Alias::Library); + let crates = std_crates_for_run_make(&run); run.builder.ensure(Std { target: run.target, crates }); } diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 68abf1e464a7a..2a01213c7d678 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -19,6 +19,7 @@ use super::compile::libstd_stamp; use super::compile::run_cargo; use super::compile::rustc_cargo; use super::compile::std_cargo; +use super::compile::std_crates_for_run_make; use super::tool::prepare_tool_cargo; use super::tool::SourceType; @@ -120,7 +121,7 @@ impl Step for Std { } fn make_run(run: RunConfig<'_>) { - let crates = run.make_run_crates(Alias::Library); + let crates = std_crates_for_run_make(&run); run.builder.ensure(Std { target: run.target, crates }); } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 23d1e0ffe2d05..cacb70134ca84 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -127,11 +127,7 @@ impl Step for Std { } fn make_run(run: RunConfig<'_>) { - // If the paths include "library", build the entire standard library. - let has_alias = - run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library")); - let crates = if has_alias { Default::default() } else { run.cargo_crates_in_set() }; - + let crates = std_crates_for_run_make(&run); run.builder.ensure(Std { compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, @@ -429,6 +425,28 @@ fn copy_self_contained_objects( target_deps } +/// Resolves standard library crates for `Std::run_make` for any build kind (like check, build, clippy, etc.). +pub fn std_crates_for_run_make(run: &RunConfig<'_>) -> Vec { + // FIXME: Extend builder tests to cover the `crates` field of `Std` instances. + if cfg!(feature = "bootstrap-self-test") { + return vec![]; + } + + let has_alias = run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library")); + let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false); + + // For no_std targets, do not add any additional crates to the compilation other than what `compile::std_cargo` already adds for no_std targets. + if target_is_no_std { + vec![] + } + // If the paths include "library", build the entire standard library. + else if has_alias { + run.make_run_crates(builder::Alias::Library) + } else { + run.cargo_crates_in_set() + } +} + /// Configure cargo to compile the standard library, adding appropriate env vars /// and such. pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, cargo: &mut Cargo) { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index cd0661e0edd61..7a122b1517043 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -108,7 +108,6 @@ impl Step for JsonDocs { builder.ensure(crate::core::build_steps::doc::Std::new( builder.top_stage, host, - builder, DocumentationFormat::Json, )); diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index bdd48fa644a8a..e233f01548346 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -563,18 +563,8 @@ pub struct Std { } impl Std { - pub(crate) fn new( - stage: u32, - target: TargetSelection, - builder: &Builder<'_>, - format: DocumentationFormat, - ) -> Self { - let crates = builder - .in_tree_crates("sysroot", Some(target)) - .into_iter() - .map(|krate| krate.name.to_string()) - .collect(); - Std { stage, target, format, crates } + pub(crate) fn new(stage: u32, target: TargetSelection, format: DocumentationFormat) -> Self { + Std { stage, target, format, crates: vec![] } } } @@ -588,6 +578,7 @@ impl Step for Std { } fn make_run(run: RunConfig<'_>) { + let crates = compile::std_crates_for_run_make(&run); run.builder.ensure(Std { stage: run.builder.top_stage, target: run.target, @@ -596,7 +587,7 @@ impl Step for Std { } else { DocumentationFormat::Html }, - crates: run.make_run_crates(Alias::Library), + crates, }); } @@ -694,13 +685,6 @@ fn doc_std( extra_args: &[&str], requested_crates: &[String], ) { - if builder.no_std(target) == Some(true) { - panic!( - "building std documentation for no_std target {target} is not supported\n\ - Set `docs = false` in the config to disable documentation, or pass `--skip library`." - ); - } - let compiler = builder.compiler(stage, builder.config.build); let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" }; diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 4942a4939af02..7698dd70468d7 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -852,7 +852,6 @@ impl Step for RustdocJSStd { builder.ensure(crate::core::build_steps::doc::Std::new( builder.top_stage, self.target, - builder, DocumentationFormat::Html, )); let _guard = builder.msg( diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index ccabcad243f7b..a61aecac8979e 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -78,13 +78,9 @@ macro_rules! std { macro_rules! doc_std { ($host:ident => $target:ident, stage = $stage:literal) => {{ - let config = configure("doc", &["A-A"], &["A-A"]); - let build = Build::new(config); - let builder = Builder::new(&build); doc::Std::new( $stage, TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))), - &builder, DocumentationFormat::Html, ) }}; diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs index f52524e7d54cd..e6460fb93d3e9 100644 --- a/src/tools/run-make-support/src/env.rs +++ b/src/tools/run-make-support/src/env.rs @@ -17,3 +17,10 @@ pub fn env_var_os(name: &str) -> OsString { None => panic!("failed to retrieve environment variable {name:?}"), } } + +/// Check if `NO_DEBUG_ASSERTIONS` is set (usually this may be set in CI jobs). +#[track_caller] +#[must_use] +pub fn no_debug_assertions() -> bool { + std::env::var_os("NO_DEBUG_ASSERTIONS").is_some() +} diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 085120764b463..36ea6125f2780 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -21,6 +21,7 @@ pub mod run; pub mod scoped_run; pub mod string; pub mod targets; +pub mod symbols; // Internally we call our fs-related support module as `fs`, but re-export its content as `rfs` // to tests to avoid colliding with commonly used `use std::fs;`. diff --git a/src/tools/run-make-support/src/symbols.rs b/src/tools/run-make-support/src/symbols.rs new file mode 100644 index 0000000000000..52119ba4991a8 --- /dev/null +++ b/src/tools/run-make-support/src/symbols.rs @@ -0,0 +1,43 @@ +use object::{self, Object, ObjectSymbol, SymbolIterator}; +use std::path::Path; + +/// Iterate through the symbols in an object file. +/// +/// Uses a callback because `SymbolIterator` does not own its data. +/// +/// Panics if `path` is not a valid object file readable by the current user. +pub fn with_symbol_iter(path: P, func: F) -> R +where + P: AsRef, + F: FnOnce(&mut SymbolIterator<'_, '_>) -> R, +{ + let raw_bytes = crate::fs::read(path); + let f = object::File::parse(raw_bytes.as_slice()).expect("unable to parse file"); + let mut iter = f.symbols(); + func(&mut iter) +} + +/// Check an object file's symbols for substrings. +/// +/// Returns `true` if any of the symbols found in the object file at +/// `path` contain a substring listed in `substrings`. +/// +/// Panics if `path` is not a valid object file readable by the current user. +pub fn any_symbol_contains(path: impl AsRef, substrings: &[&str]) -> bool { + with_symbol_iter(path, |syms| { + for sym in syms { + for substring in substrings { + if sym + .name_bytes() + .unwrap() + .windows(substring.len()) + .any(|x| x == substring.as_bytes()) + { + eprintln!("{:?} contains {}", sym, substring); + return true; + } + } + } + false + }) +} diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 7284feadf9cdd..efb51b4360bbd 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -10,7 +10,6 @@ run-make/dep-info-spaces/Makefile run-make/dep-info/Makefile run-make/emit-to-stdout/Makefile run-make/extern-fn-reachable/Makefile -run-make/fmt-write-bloat/Makefile run-make/foreign-double-unwind/Makefile run-make/foreign-exceptions/Makefile run-make/incr-add-rust-src-component/Makefile diff --git a/tests/run-make/fmt-write-bloat/Makefile b/tests/run-make/fmt-write-bloat/Makefile deleted file mode 100644 index 70e04b9af51fe..0000000000000 --- a/tests/run-make/fmt-write-bloat/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -include ../tools.mk - -# ignore-windows - -ifeq ($(shell $(RUSTC) -vV | grep 'host: $(TARGET)'),) - -# Don't run this test when cross compiling. -all: - -else - -NM = nm - -PANIC_SYMS = panic_bounds_check Debug - -# Allow for debug_assert!() in debug builds of std. -ifdef NO_DEBUG_ASSERTIONS -PANIC_SYMS += panicking panic_fmt pad_integral Display Debug -endif - -all: main.rs - $(RUSTC) $< -O - $(NM) $(call RUN_BINFILE,main) | $(CGREP) -v $(PANIC_SYMS) - -endif diff --git a/tests/run-make/fmt-write-bloat/rmake.rs b/tests/run-make/fmt-write-bloat/rmake.rs new file mode 100644 index 0000000000000..120c1cbccdd3c --- /dev/null +++ b/tests/run-make/fmt-write-bloat/rmake.rs @@ -0,0 +1,35 @@ +//! Before #78122, writing any `fmt::Arguments` would trigger the inclusion of `usize` formatting +//! and padding code in the resulting binary, because indexing used in `fmt::write` would generate +//! code using `panic_bounds_check`, which prints the index and length. +//! +//! These bounds checks are not necessary, as `fmt::Arguments` never contains any out-of-bounds +//! indexes. The test is a `run-make` test, because it needs to check the result after linking. A +//! codegen or assembly test doesn't check the parts that will be pulled in from `core` by the +//! linker. +//! +//! In this test, we try to check that the `usize` formatting and padding code are not present in +//! the final binary by checking that panic symbols such as `panic_bounds_check` are **not** +//! present. +//! +//! Some CI jobs try to run faster by disabling debug assertions (through setting +//! `NO_DEBUG_ASSERTIONS=1`). If debug assertions are disabled, then we can check for the absence of +//! additional `usize` formatting and padding related symbols. + +// Reason: This test is `ignore-windows` because the `no_std` test (using `#[link(name = "c")])` +// doesn't link on windows. +//@ ignore-windows +//@ ignore-cross-compile + +use run_make_support::{env::no_debug_assertions, rustc, symbols::any_symbol_contains}; + +fn main() { + rustc().input("main.rs").opt().run(); + // panic machinery identifiers, these should not appear in the final binary + let mut panic_syms = vec!["panic_bounds_check", "Debug"]; + if no_debug_assertions() { + // if debug assertions are allowed, we need to allow these, + // otherwise, add them to the list of symbols to deny. + panic_syms.extend_from_slice(&["panicking", "panic_fmt", "pad_integral", "Display"]); + } + assert!(!any_symbol_contains("main", &panic_syms)); +}