From a4e87e940620bc0e61caa7c42b1edc53c0aee7cb Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 10 Sep 2025 17:11:47 -0400 Subject: Support `#[rustc_align_static]` inside `thread_local!` --- src/tools/miri/tests/pass/static_align.rs | 60 +++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'src') diff --git a/src/tools/miri/tests/pass/static_align.rs b/src/tools/miri/tests/pass/static_align.rs index f292f028568..bc6a9bf8af7 100644 --- a/src/tools/miri/tests/pass/static_align.rs +++ b/src/tools/miri/tests/pass/static_align.rs @@ -1,4 +1,7 @@ #![feature(static_align)] +#![deny(non_upper_case_globals)] + +use std::cell::Cell; // When a static uses `align(N)`, its address should be a multiple of `N`. @@ -8,7 +11,64 @@ static FOO: u64 = 0; #[rustc_align_static(512)] static BAR: u64 = 0; +struct HasDrop(*const HasDrop); + +impl Drop for HasDrop { + fn drop(&mut self) { + assert_eq!(core::ptr::from_mut(self).cast_const(), self.0); + } +} + +thread_local! { + #[rustc_align_static(4096)] + static LOCAL: u64 = 0; + + #[allow(unused_mut, reason = "test attribute handling")] + #[cfg_attr(true, rustc_align_static(4096))] + static CONST_LOCAL: u64 = const { 0 }; + + #[cfg_attr(any(true), cfg_attr(true, rustc_align_static(4096)))] + #[allow(unused_mut, reason = "test attribute handling")] + static HASDROP_LOCAL: Cell = Cell::new(HasDrop(core::ptr::null())); + + /// I love doc comments. + #[allow(unused_mut, reason = "test attribute handling")] + #[cfg_attr(all(), + cfg_attr(any(true), + cfg_attr(true, rustc_align_static(4096))))] + #[allow(unused_mut, reason = "test attribute handling")] + /// I love doc comments. + static HASDROP_CONST_LOCAL: Cell = const { Cell::new(HasDrop(core::ptr::null())) }; + + #[cfg_attr(true,)] + #[cfg_attr(false,)] + #[cfg_attr( + true, + rustc_align_static(32), + cfg_attr(true, allow(non_upper_case_globals, reason = "test attribute handling")), + cfg_attr(false,) + )] + #[cfg_attr(false, rustc_align_static(0))] + static more_attr_testing: u64 = 0; +} + +fn thread_local_ptr(key: &'static std::thread::LocalKey) -> *const T { + key.with(|local| core::ptr::from_ref::(local)) +} + fn main() { assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256)); assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512)); + + assert!(thread_local_ptr(&LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&CONST_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&HASDROP_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&HASDROP_CONST_LOCAL).addr().is_multiple_of(4096)); + assert!(thread_local_ptr(&more_attr_testing).addr().is_multiple_of(32)); + + // Test that address (and therefore alignment) is maintained during drop + let hasdrop_ptr = thread_local_ptr(&HASDROP_LOCAL); + core::mem::forget(HASDROP_LOCAL.replace(HasDrop(hasdrop_ptr.cast()))); + let hasdrop_const_ptr = thread_local_ptr(&HASDROP_CONST_LOCAL); + core::mem::forget(HASDROP_CONST_LOCAL.replace(HasDrop(hasdrop_const_ptr.cast()))); } -- cgit 1.4.1-3-g733a5 From 69619535d9dbad6a7f5e622bfd3e3c3fb190e0aa Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Oct 2025 17:36:53 +0200 Subject: Switch `citool` to 2024 edition --- src/ci/citool/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml index f61243a4d71..078b877e44b 100644 --- a/src/ci/citool/Cargo.toml +++ b/src/ci/citool/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "citool" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] anyhow = "1" -- cgit 1.4.1-3-g733a5 From 4baf9208a129e5ea8b3973ce2eeb55fdd7404aea Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Oct 2025 17:38:36 +0200 Subject: Initialize llvm submodule if not already the case to run citool --- src/ci/citool/src/main.rs | 4 +++- src/ci/citool/src/utils.rs | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index fe1b36673a1..255d39846da 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -24,7 +24,7 @@ use crate::github::JobInfoResolver; use crate::jobs::RunType; use crate::metrics::{JobMetrics, download_auto_job_metrics, download_job_metrics, load_metrics}; use crate::test_dashboard::generate_test_dashboard; -use crate::utils::{load_env_var, output_details}; +use crate::utils::{init_submodule_if_needed, load_env_var, output_details}; const CI_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/.."); pub const DOCKER_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../docker"); @@ -121,6 +121,8 @@ fn run_workflow_locally(db: JobDatabase, job_type: JobType, name: String) -> any (key.clone(), value) })); + init_submodule_if_needed("src/llvm-project/")?; + let mut cmd = Command::new(Path::new(DOCKER_DIRECTORY).join("run.sh")); cmd.arg(job.image()); cmd.envs(custom_env); diff --git a/src/ci/citool/src/utils.rs b/src/ci/citool/src/utils.rs index 3176cb62f60..43b220255dc 100644 --- a/src/ci/citool/src/utils.rs +++ b/src/ci/citool/src/utils.rs @@ -1,5 +1,7 @@ use std::borrow::Cow; +use std::convert::AsRef; use std::path::Path; +use std::process::Command; use anyhow::Context; @@ -34,3 +36,19 @@ where pub fn normalize_path_delimiters(name: &str) -> Cow<'_, str> { if name.contains("\\") { name.replace('\\', "/").into() } else { name.into() } } + +pub fn init_submodule_if_needed>(path_to_submodule: P) -> anyhow::Result<()> { + let path_to_submodule = path_to_submodule.as_ref(); + + if let Ok(mut iter) = path_to_submodule.read_dir() + && iter.any(|entry| entry.is_ok()) + { + // Seems like the submodule is already initialized, nothing to be done here. + return Ok(()); + } + let mut child = Command::new("git") + .args(&["submodule", "update", "--init"]) + .arg(path_to_submodule) + .spawn()?; + if !child.wait()?.success() { Err(anyhow::anyhow!("git command failed")) } else { Ok(()) } +} -- cgit 1.4.1-3-g733a5 From 94f00f4e4a0240bc7b8284c78482e37af252309a Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 1 Oct 2025 11:32:54 -0400 Subject: Fix memory leak in `os` impl --- library/std/src/sys/thread_local/os.rs | 103 ++++++++++++++------- src/tools/miri/tests/pass/thread_local-panic.rs | 8 ++ .../miri/tests/pass/thread_local-panic.stderr | 5 + 3 files changed, 85 insertions(+), 31 deletions(-) create mode 100644 src/tools/miri/tests/pass/thread_local-panic.rs create mode 100644 src/tools/miri/tests/pass/thread_local-panic.stderr (limited to 'src') diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index b488f12fe84..88bb5ae7c65 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -1,9 +1,12 @@ use super::key::{Key, LazyKey, get, set}; use super::{abort_on_dtor_unwind, guard}; -use crate::alloc::Layout; +use crate::alloc::{self, Layout}; use crate::cell::Cell; use crate::marker::PhantomData; -use crate::ptr; +use crate::mem::ManuallyDrop; +use crate::ops::Deref; +use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; +use crate::ptr::{self, NonNull}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -110,6 +113,60 @@ pub const fn value_align() -> usize { crate::mem::align_of::>() } +/// Equivalent to `Box>`, but potentially over-aligned. +struct AlignedBox { + ptr: NonNull>, +} + +impl AlignedBox { + #[inline] + fn new(v: Value) -> Self { + let layout = Layout::new::>().align_to(ALIGN).unwrap(); + + let ptr: *mut Value = (unsafe { alloc::alloc(layout) }).cast(); + let Some(ptr) = NonNull::new(ptr) else { + alloc::handle_alloc_error(layout); + }; + unsafe { ptr.write(v) }; + Self { ptr } + } + + #[inline] + fn into_raw(b: Self) -> *mut Value { + let md = ManuallyDrop::new(b); + md.ptr.as_ptr() + } + + #[inline] + unsafe fn from_raw(ptr: *mut Value) -> Self { + Self { ptr: unsafe { NonNull::new_unchecked(ptr) } } + } +} + +impl Deref for AlignedBox { + type Target = Value; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { &*(self.ptr.as_ptr()) } + } +} + +impl Drop for AlignedBox { + #[inline] + fn drop(&mut self) { + let layout = Layout::new::>().align_to(ALIGN).unwrap(); + + unsafe { + let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place())); + alloc::dealloc(self.ptr.as_ptr().cast(), layout); + if let Err(payload) = unwind_result { + resume_unwind(payload); + } + } + } +} + impl Storage { pub const fn new() -> Storage { Storage { key: LazyKey::new(Some(destroy_value::)), marker: PhantomData } @@ -148,15 +205,11 @@ impl Storage { return ptr::null(); } - // Manually allocate with the requested alignment - let layout = Layout::new::>().align_to(ALIGN).unwrap(); - let ptr: *mut Value = (unsafe { crate::alloc::alloc(layout) }).cast(); - if ptr.is_null() { - crate::alloc::handle_alloc_error(layout); - } - unsafe { - ptr.write(Value { value: i.and_then(Option::take).unwrap_or_else(f), key }); - } + let value = AlignedBox::::new(Value { + value: i.and_then(Option::take).unwrap_or_else(f), + key, + }); + let ptr = AlignedBox::into_raw(value); // SAFETY: // * key came from a `LazyKey` and is thus correct. @@ -174,10 +227,7 @@ impl Storage { // initializer has already returned and the next scope only starts // after we return the pointer. Therefore, there can be no references // to the old value. - unsafe { - old.drop_in_place(); - crate::alloc::dealloc(old.cast(), layout); - } + drop(unsafe { AlignedBox::::from_raw(old) }); } // SAFETY: We just created this value above. @@ -196,22 +246,13 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) // Note that to prevent an infinite loop we reset it back to null right // before we return from the destructor ourselves. abort_on_dtor_unwind(|| { - let value_ptr: *mut Value = ptr.cast(); - unsafe { - let key = (*value_ptr).key; - - // SAFETY: `key` is the TLS key `ptr` was stored under. - set(key, ptr::without_provenance_mut(1)); - - // drop and deallocate the value - let layout = - Layout::from_size_align_unchecked(crate::mem::size_of::>(), ALIGN); - value_ptr.drop_in_place(); - crate::alloc::dealloc(ptr, layout); - - // SAFETY: `key` is the TLS key `ptr` was stored under. - set(key, ptr::null_mut()); - }; + let ptr = unsafe { AlignedBox::::from_raw(ptr as *mut Value) }; + let key = ptr.key; + // SAFETY: `key` is the TLS key `ptr` was stored under. + unsafe { set(key, ptr::without_provenance_mut(1)) }; + drop(ptr); + // SAFETY: `key` is the TLS key `ptr` was stored under. + unsafe { set(key, ptr::null_mut()) }; // Make sure that the runtime cleanup will be performed // after the next round of TLS destruction. guard::enable(); diff --git a/src/tools/miri/tests/pass/thread_local-panic.rs b/src/tools/miri/tests/pass/thread_local-panic.rs new file mode 100644 index 00000000000..549acd23987 --- /dev/null +++ b/src/tools/miri/tests/pass/thread_local-panic.rs @@ -0,0 +1,8 @@ +thread_local! { + static LOCAL: u64 = panic!(); + +} + +fn main() { + let _ = std::panic::catch_unwind(|| LOCAL.with(|_| {})); +} diff --git a/src/tools/miri/tests/pass/thread_local-panic.stderr b/src/tools/miri/tests/pass/thread_local-panic.stderr new file mode 100644 index 00000000000..e69340a8102 --- /dev/null +++ b/src/tools/miri/tests/pass/thread_local-panic.stderr @@ -0,0 +1,5 @@ + +thread 'main' ($TID) panicked at tests/pass/thread_local-panic.rs:LL:CC: +explicit panic +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect -- cgit 1.4.1-3-g733a5 From 5991c8703d01cc73c31c51fef5d92b93ac1391b4 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:14:45 +0200 Subject: Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/doc/book b/src/doc/book index 33f1af40cc4..1d7c3e6abec 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 33f1af40cc44dde7e3e892f7a508e6f427d2cbc6 +Subproject commit 1d7c3e6abec2d5a9bfac798b29b7855b95025426 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index aa6ce337c0a..e2ed891f003 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit aa6ce337c0adf7a63e33960d184270f2a45ab9ef +Subproject commit e2ed891f00361efc26616d82590b1c85d7a8920e diff --git a/src/doc/nomicon b/src/doc/nomicon index f17a018b998..23fc2682f8f 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit f17a018b9989430967d1c58e9a12c51169abc744 +Subproject commit 23fc2682f8fcb887f77d0eaabba708809f834c11 diff --git a/src/doc/reference b/src/doc/reference index cc7247d8dfa..e11adf6016a 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit cc7247d8dfaef4c39000bb12c55c32ba5b5ba976 +Subproject commit e11adf6016a362766eea5a3f9832e193994dd0c8 -- cgit 1.4.1-3-g733a5 From 59c4dfe59d587f0f09746958bf9a16c271cc02a7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 1 Oct 2025 21:45:50 +1000 Subject: Forbid `//@ compile-flags: -Cincremental=` in tests Tests should not try to manually enable incremental compilation with `-Cincremental`, because that typically results in stray directories being created in the repository root. Instead, use the `//@ incremental` directive, which instructs compiletest to handle the details of passing `-Cincremental` with a fresh directory. --- src/tools/compiletest/src/directives.rs | 10 +++++++++- .../compiletest-self-test/compile-flags-incremental.rs | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/ui/compiletest-self-test/compile-flags-incremental.rs (limited to 'src') diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index e6916610190..0d85e76c2cc 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -415,10 +415,18 @@ impl TestProps { config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile) { let flags = split_flags(&flags); - for flag in &flags { + for (i, flag) in flags.iter().enumerate() { if flag == "--edition" || flag.starts_with("--edition=") { panic!("you must use `//@ edition` to configure the edition"); } + if (flag == "-C" + && flags.get(i + 1).is_some_and(|v| v.starts_with("incremental="))) + || flag.starts_with("-Cincremental=") + { + panic!( + "you must use `//@ incremental` to enable incremental compilation" + ); + } } self.compile_flags.extend(flags); } diff --git a/tests/ui/compiletest-self-test/compile-flags-incremental.rs b/tests/ui/compiletest-self-test/compile-flags-incremental.rs new file mode 100644 index 00000000000..62a1ad84d8f --- /dev/null +++ b/tests/ui/compiletest-self-test/compile-flags-incremental.rs @@ -0,0 +1,17 @@ +//@ revisions: good bad bad-space +//@ check-pass + +//@[bad] compile-flags: -Cincremental=true +//@[bad] should-fail + +//@[bad-space] compile-flags: -C incremental=dir +//@[bad-space] should-fail + +fn main() {} + +// Tests should not try to manually enable incremental compilation with +// `-Cincremental`, because that typically results in stray directories being +// created in the repository root. +// +// Instead, use the `//@ incremental` directive, which instructs compiletest +// to handle the details of passing `-Cincremental` with a fresh directory. -- cgit 1.4.1-3-g733a5