From 07d3e923ac68ba6daf516f4298ae6b00471696f1 Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 3 Sep 2025 20:10:43 -0400 Subject: [PATCH 01/12] Unstably constify ptr::drop_in_place and related methods --- library/core/src/mem/manually_drop.rs | 7 +++- library/core/src/mem/maybe_uninit.rs | 13 +++++- library/core/src/ptr/mod.rs | 8 +++- library/core/src/ptr/mut_ptr.rs | 8 +++- library/core/src/ptr/non_null.rs | 8 +++- library/coretests/tests/lib.rs | 1 + library/coretests/tests/ptr.rs | 41 ++++++++++++++++++- .../drop_in_place_retag.stderr | 6 ++- .../unaligned_pointers/drop_in_place.stderr | 6 ++- 9 files changed, 84 insertions(+), 14 deletions(-) diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 8868f05f1b98f..334a4b7119a11 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -1,3 +1,4 @@ +use crate::marker::Destruct; use crate::ops::{Deref, DerefMut, DerefPure}; use crate::ptr; @@ -249,7 +250,11 @@ impl ManuallyDrop { /// [pinned]: crate::pin #[stable(feature = "manually_drop", since = "1.20.0")] #[inline] - pub unsafe fn drop(slot: &mut ManuallyDrop) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn drop(slot: &mut ManuallyDrop) + where + T: [const] Destruct, + { // SAFETY: we are dropping the value pointed to by a mutable reference // which is guaranteed to be valid for writes. // It is up to the caller to make sure that `slot` isn't dropped again. diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index c160360cfacf9..8d666e9b130c0 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,4 +1,5 @@ use crate::any::type_name; +use crate::marker::Destruct; use crate::mem::ManuallyDrop; use crate::{fmt, intrinsics, ptr, slice}; @@ -714,7 +715,11 @@ impl MaybeUninit { /// /// [`assume_init`]: MaybeUninit::assume_init #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] - pub unsafe fn assume_init_drop(&mut self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn assume_init_drop(&mut self) + where + T: [const] Destruct, + { // SAFETY: the caller must guarantee that `self` is initialized and // satisfies all invariants of `T`. // Dropping the value in place is safe if that is the case. @@ -1390,7 +1395,11 @@ impl [MaybeUninit] { /// behaviour. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub unsafe fn assume_init_drop(&mut self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn assume_init_drop(&mut self) + where + T: [const] Destruct, + { if !self.is_empty() { // SAFETY: the caller must guarantee that every element of `self` // is initialized and satisfies all invariants of `T`. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 6b94088cb5679..8c20b5e58b8f2 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -403,7 +403,7 @@ use crate::cmp::Ordering; use crate::intrinsics::const_eval_select; -use crate::marker::{FnPtr, PointeeSized}; +use crate::marker::{Destruct, FnPtr, PointeeSized}; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::{fmt, hash, intrinsics, ub_checks}; @@ -801,7 +801,11 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { #[lang = "drop_in_place"] #[allow(unconditional_recursion)] #[rustc_diagnostic_item = "ptr_drop_in_place"] -pub unsafe fn drop_in_place(to_drop: *mut T) { +#[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] +pub const unsafe fn drop_in_place(to_drop: *mut T) +where + T: [const] Destruct, +{ // Code here does not matter - this is replaced by the // real drop glue by the compiler. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ce6eee4f911ed..1e3bc2939320d 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1,7 +1,7 @@ use super::*; use crate::cmp::Ordering::{Equal, Greater, Less}; use crate::intrinsics::const_eval_select; -use crate::marker::PointeeSized; +use crate::marker::{Destruct, PointeeSized}; use crate::mem::{self, SizedTypeProperties}; use crate::slice::{self, SliceIndex}; @@ -1438,8 +1438,12 @@ impl *mut T { /// /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] #[inline(always)] - pub unsafe fn drop_in_place(self) { + pub const unsafe fn drop_in_place(self) + where + T: [const] Destruct, + { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { drop_in_place(self) } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 10f83120428b9..a762e969b52dc 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1,5 +1,5 @@ use crate::cmp::Ordering; -use crate::marker::{PointeeSized, Unsize}; +use crate::marker::{Destruct, PointeeSized, Unsize}; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{CoerceUnsized, DispatchFromDyn}; @@ -1118,7 +1118,11 @@ impl NonNull { /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - pub unsafe fn drop_in_place(self) { + #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] + pub const unsafe fn drop_in_place(self) + where + T: [const] Destruct, + { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { ptr::drop_in_place(self.as_ptr()) } } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b5658a9970fee..c8b940cf3728b 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -17,6 +17,7 @@ #![feature(clone_to_uninit)] #![feature(const_convert)] #![feature(const_destruct)] +#![feature(const_drop_in_place)] #![feature(const_eval_select)] #![feature(const_ops)] #![feature(const_option_ops)] diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index c13fb96a67f92..217e370ee8974 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use core::marker::Freeze; -use core::mem::MaybeUninit; +use core::mem::{ManuallyDrop, MaybeUninit}; use core::num::NonZero; use core::ptr; use core::ptr::*; @@ -1036,3 +1036,42 @@ fn test_ptr_default() { let default = PtrMutDefaultTest::default(); assert!(default.ptr.is_null()); } + +#[test] +fn test_const_drop_in_place() { + const COUNTER: usize = { + let mut counter = 0; + let counter_ptr = &raw mut counter; + + // only exists to make `Drop` indirect impl + #[allow(dead_code)] + struct Test(Dropped); + + struct Dropped(*mut usize); + impl const Drop for Dropped { + fn drop(&mut self) { + unsafe { + *self.0 += 1; + } + } + } + + let mut one = ManuallyDrop::new(Test(Dropped(counter_ptr))); + let mut two = ManuallyDrop::new(Test(Dropped(counter_ptr))); + let mut three = ManuallyDrop::new(Test(Dropped(counter_ptr))); + assert!(counter == 0); + unsafe { + ManuallyDrop::drop(&mut one); + } + assert!(counter == 1); + unsafe { + ManuallyDrop::drop(&mut two); + } + assert!(counter == 2); + unsafe { + ManuallyDrop::drop(&mut three); + } + counter + }; + assert_eq!(COUNTER, 3); +} diff --git a/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr b/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr index ec578db2d0a8e..e586d3f03994f 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/drop_in_place_retag.stderr @@ -1,8 +1,10 @@ error: Undefined Behavior: trying to retag from for Unique permission at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location --> RUSTLIB/core/src/ptr/mod.rs:LL:CC | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of retag at ALLOC[0x0..0x1] +LL | / pub const unsafe fn drop_in_place(to_drop: *mut T) +LL | | where +LL | | T: [const] Destruct, + | |________________________^ this error occurs as part of retag at ALLOC[0x0..0x1] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr b/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr index 72aeb0fdf8e49..ea144f206643a 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr +++ b/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.stderr @@ -1,8 +1,10 @@ error: Undefined Behavior: constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN) --> RUSTLIB/core/src/ptr/mod.rs:LL:CC | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | / pub const unsafe fn drop_in_place(to_drop: *mut T) +LL | | where +LL | | T: [const] Destruct, + | |________________________^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information From f47c48048d6ba9d96dd4138803595284443d7708 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:35:14 +0000 Subject: [PATCH 02/12] Reduce scope of unsafe block in cg_llvm allocator codegen --- compiler/rustc_codegen_llvm/src/allocator.rs | 42 ++++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 824aa501036d4..486ee817a1c79 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -95,28 +95,28 @@ pub(crate) unsafe fn codegen( &CodegenFnAttrs::new(), ); - unsafe { - // __rust_alloc_error_handler_should_panic_v2 - create_const_value_function( - tcx, - &cx, - &mangle_internal_symbol(tcx, OomStrategy::SYMBOL), - &i8, - &llvm::LLVMConstInt(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as u64, FALSE), - ); + // __rust_alloc_error_handler_should_panic_v2 + create_const_value_function( + tcx, + &cx, + &mangle_internal_symbol(tcx, OomStrategy::SYMBOL), + &i8, + unsafe { + llvm::LLVMConstInt(i8, tcx.sess.opts.unstable_opts.oom.should_panic() as u64, FALSE) + }, + ); - // __rust_no_alloc_shim_is_unstable_v2 - create_wrapper_function( - tcx, - &cx, - &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), - None, - &[], - None, - false, - &CodegenFnAttrs::new(), - ); - } + // __rust_no_alloc_shim_is_unstable_v2 + create_wrapper_function( + tcx, + &cx, + &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + None, + &[], + None, + false, + &CodegenFnAttrs::new(), + ); if tcx.sess.opts.debuginfo != DebugInfo::None { let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod); From 116f4ae171e292423304ee6842a11c48a4fff5ba Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:44:01 +0000 Subject: [PATCH 03/12] Support #[alloc_error_handler] without the allocator shim Currently it is possible to avoid linking the allocator shim when __rust_no_alloc_shim_is_unstable_v2 is defined when linking rlibs directly as some build systems need. However this requires liballoc to be compiled with --cfg no_global_oom_handling, which places huge restrictions on what functions you can call and makes it impossible to use libstd. Or alternatively you have to define __rust_alloc_error_handler and (when using libstd) __rust_alloc_error_handler_should_panic using #[rustc_std_internal_symbol]. With this commit you can either use libstd and define __rust_alloc_error_handler_should_panic or not use libstd and use #[alloc_error_handler] instead. Both options are still unstable though. Eventually the alloc_error_handler may either be removed entirely (though the PR for that has been stale for years now) or we may start using weak symbols for it instead. For the latter case this commit is a prerequisite anyway. --- compiler/rustc_ast/src/expand/allocator.rs | 8 +----- .../src/alloc_error_handler.rs | 5 ++-- .../rustc_codegen_cranelift/src/allocator.rs | 28 ++++++++++--------- compiler/rustc_codegen_gcc/src/allocator.rs | 24 ++++++++-------- compiler/rustc_codegen_llvm/src/allocator.rs | 28 ++++++++++--------- .../src/back/symbol_export.rs | 6 ++-- compiler/rustc_metadata/src/creader.rs | 24 ++++++++-------- library/alloc/src/alloc.rs | 4 +-- library/std/src/alloc.rs | 7 +++-- src/tools/miri/src/shims/foreign_items.rs | 17 ++++++----- .../fail/alloc/alloc_error_handler.stderr | 2 +- .../alloc/alloc_error_handler_custom.stderr | 2 +- .../alloc/alloc_error_handler_no_std.stderr | 2 +- tests/run-make/bin-emit-no-symbols/rmake.rs | 2 +- tests/ui/sanitizer/dataflow-abilist.txt | 10 ------- 15 files changed, 83 insertions(+), 86 deletions(-) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index 7dee2ed17b4be..a3f9ddb154f71 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -15,13 +15,7 @@ pub fn default_fn_name(base: Symbol) -> String { format!("__rdl_{base}") } -pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'static str { - match alloc_error_handler_kind { - AllocatorKind::Global => "__rg_oom", - AllocatorKind::Default => "__rdl_oom", - } -} - +pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler; pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2"; pub enum AllocatorTy { diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 35ef6be095e9c..40946f3b2791a 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -1,3 +1,4 @@ +use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, global_fn_name}; use rustc_ast::{ self as ast, Fn, FnHeader, FnSig, Generics, ItemKind, Safety, Stmt, StmtKind, TyKind, }; @@ -55,7 +56,7 @@ pub(crate) fn expand( } // #[rustc_std_internal_symbol] -// unsafe fn __rg_oom(size: usize, align: usize) -> ! { +// unsafe fn __rust_alloc_error_handler(size: usize, align: usize) -> ! { // handler(core::alloc::Layout::from_size_align_unchecked(size, align)) // } fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span) -> Stmt { @@ -84,7 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span let kind = ItemKind::Fn(Box::new(Fn { defaultness: ast::Defaultness::Final, sig, - ident: Ident::from_str_and_span("__rg_oom", span), + ident: Ident::from_str_and_span(&global_fn_name(ALLOC_ERROR_HANDLER), span), generics: Generics::default(), contract: None, body, diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 04f1129d87c1f..9533f870434ab 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -3,8 +3,8 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_ast::expand::allocator::{ - ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - alloc_error_handler_name, default_fn_name, global_fn_name, + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, + default_fn_name, global_fn_name, }; use rustc_codegen_ssa::base::allocator_kind_for_codegen; use rustc_session::config::OomStrategy; @@ -72,17 +72,19 @@ fn codegen_inner( } } - let sig = Signature { - call_conv: module.target_config().default_call_conv, - params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)], - returns: vec![], - }; - crate::common::create_wrapper_function( - module, - sig, - &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - &mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)), - ); + if alloc_error_handler_kind == AllocatorKind::Default { + let sig = Signature { + call_conv: module.target_config().default_call_conv, + params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)], + returns: vec![], + }; + crate::common::create_wrapper_function( + module, + sig, + &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), + &mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER)), + ); + } { let sig = Signature { diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index 2a95a7368aac6..3f99612fcdb02 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -2,8 +2,8 @@ use gccjit::FnAttribute; use gccjit::{Context, FunctionType, RValue, ToRValue, Type}; use rustc_ast::expand::allocator::{ - ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - alloc_error_handler_name, default_fn_name, global_fn_name, + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, + default_fn_name, global_fn_name, }; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; @@ -61,15 +61,17 @@ pub(crate) unsafe fn codegen( } } - // FIXME(bjorn3): Add noreturn attribute - create_wrapper_function( - tcx, - context, - &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))), - &[usize, usize], - None, - ); + if alloc_error_handler_kind == AllocatorKind::Default { + // FIXME(bjorn3): Add noreturn attribute + create_wrapper_function( + tcx, + context, + &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), + Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))), + &[usize, usize], + None, + ); + } create_const_value_function( tcx, diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 486ee817a1c79..b645db3f4a8b8 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -1,7 +1,7 @@ use libc::c_uint; use rustc_ast::expand::allocator::{ - ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - alloc_error_handler_name, default_fn_name, global_fn_name, + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, + default_fn_name, global_fn_name, }; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; @@ -83,17 +83,19 @@ pub(crate) unsafe fn codegen( } } - // rust alloc error handler - create_wrapper_function( - tcx, - &cx, - &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))), - &[usize, usize], // size, align - None, - true, - &CodegenFnAttrs::new(), - ); + if alloc_error_handler_kind == AllocatorKind::Default { + // rust alloc error handler + create_wrapper_function( + tcx, + &cx, + &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), + Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))), + &[usize, usize], // size, align + None, + true, + &CodegenFnAttrs::new(), + ); + } // __rust_alloc_error_handler_should_panic_v2 create_const_value_function( diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index b49e67217fb01..5bc18e2d7f8bb 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -1,7 +1,9 @@ use std::collections::hash_map::Entry::*; use rustc_abi::{CanonAbi, X86Call}; -use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name}; +use rustc_ast::expand::allocator::{ + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name, +}; use rustc_data_structures::unord::UnordMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId}; @@ -498,7 +500,7 @@ pub(crate) fn allocator_shim_symbols( .iter() .map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str())) .chain([ - mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), + mangle_internal_symbol(tcx, global_fn_name(ALLOC_ERROR_HANDLER).as_str()), mangle_internal_symbol(tcx, OomStrategy::SYMBOL), mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), ]) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 7650acbd292d2..87d64817466d9 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use std::time::Duration; use std::{cmp, env, iter}; -use rustc_ast::expand::allocator::{AllocatorKind, alloc_error_handler_name, global_fn_name}; +use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, AllocatorKind, global_fn_name}; use rustc_ast::{self as ast, *}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::owned_slice::OwnedSlice; @@ -1087,17 +1087,17 @@ impl CStore { } spans => !spans.is_empty(), }; - self.has_alloc_error_handler = match &*fn_spans( - krate, - Symbol::intern(alloc_error_handler_name(AllocatorKind::Global)), - ) { - [span1, span2, ..] => { - tcx.dcx() - .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 }); - true - } - spans => !spans.is_empty(), - }; + self.has_alloc_error_handler = + match &*fn_spans(krate, Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER))) { + [span1, span2, ..] => { + tcx.dcx().emit_err(errors::NoMultipleAllocErrorHandler { + span2: *span2, + span1: *span1, + }); + true + } + spans => !spans.is_empty(), + }; // Check to see if we actually need an allocator. This desire comes // about through the `#![needs_allocator]` attribute and is typically diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 65c8206e9d462..39450f69ce30a 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -361,7 +361,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { unsafe extern "Rust" { // This is the magic symbol to call the global alloc error handler. rustc generates // it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the - // default implementations below (`__rdl_oom`) otherwise. + // default implementations below (`__rdl_alloc_error_handler`) otherwise. #[rustc_std_internal_symbol] fn __rust_alloc_error_handler(size: usize, align: usize) -> !; } @@ -425,7 +425,7 @@ pub mod __alloc_error_handler { // called via generated `__rust_alloc_error_handler` if there is no // `#[alloc_error_handler]`. #[rustc_std_internal_symbol] - pub unsafe fn __rdl_oom(size: usize, _align: usize) -> ! { + pub unsafe fn __rdl_alloc_error_handler(size: usize, _align: usize) -> ! { unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 1d61630269ac3..daa25c5a50dd6 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -358,9 +358,10 @@ fn default_alloc_error_hook(layout: Layout) { // This is the default path taken on OOM, and the only path taken on stable with std. // Crucially, it does *not* call any user-defined code, and therefore users do not have to // worry about allocation failure causing reentrancy issues. That makes it different from - // the default `__rdl_oom` defined in alloc (i.e., the default alloc error handler that is - // called when there is no `#[alloc_error_handler]`), which triggers a regular panic and - // thus can invoke a user-defined panic hook, executing arbitrary user-defined code. + // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error + // handler that is called when there is no `#[alloc_error_handler]`), which triggers a + // regular panic and thus can invoke a user-defined panic hook, executing arbitrary + // user-defined code. rtprintpanic!("memory allocation of {} bytes failed\n", layout.size()); } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index eca8cccf5efc4..467ac2805fe5e 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::path::Path; use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size}; -use rustc_ast::expand::allocator::alloc_error_handler_name; +use rustc_ast::expand::allocator::AllocatorKind; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; use rustc_hir::def_id::CrateNum; @@ -51,6 +51,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Some shims forward to other MIR bodies. match link_name.as_str() { + // FIXME should use global_fn_name, but mangle_internal_symbol requires a static str. name if name == this.mangle_internal_symbol("__rust_alloc_error_handler") => { // Forward to the right symbol that implements this function. let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else { @@ -59,12 +60,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set" ); }; - let name = Symbol::intern( - this.mangle_internal_symbol(alloc_error_handler_name(handler_kind)), - ); - let handler = - this.lookup_exported_symbol(name)?.expect("missing alloc error handler symbol"); - return interp_ok(Some(handler)); + if handler_kind == AllocatorKind::Default { + let name = + Symbol::intern(this.mangle_internal_symbol("__rdl_alloc_error_handler")); + let handler = this + .lookup_exported_symbol(name)? + .expect("missing alloc error handler symbol"); + return interp_ok(Some(handler)); + } } _ => {} } diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr index bbf5d14a98a1e..7f7fc63ac0ddd 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr @@ -7,7 +7,7 @@ LL | crate::process::abort() | = note: BACKTRACE: = note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC - = note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC + = note: inside `std::alloc::_::__rust_alloc_error_handler` at RUSTLIB/std/src/alloc.rs:LL:CC = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr index a2a4be30eca4a..f8a96758aa2d3 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr @@ -7,7 +7,7 @@ LL | core::intrinsics::abort(); | = note: BACKTRACE: = note: inside `alloc_error_handler` at tests/fail/alloc/alloc_error_handler_custom.rs:LL:CC -note: inside `_::__rg_oom` +note: inside `_::__rust_alloc_error_handler` --> tests/fail/alloc/alloc_error_handler_custom.rs:LL:CC | LL | #[alloc_error_handler] diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr index 45ba366acae32..488b1d879e874 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr @@ -9,7 +9,7 @@ LL | core::intrinsics::abort(); | = note: BACKTRACE: = note: inside `panic_handler` at tests/fail/alloc/alloc_error_handler_no_std.rs:LL:CC - = note: inside `alloc::alloc::__alloc_error_handler::__rdl_oom` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::__alloc_error_handler::__rdl_alloc_error_handler` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC note: inside `miri_start` diff --git a/tests/run-make/bin-emit-no-symbols/rmake.rs b/tests/run-make/bin-emit-no-symbols/rmake.rs index 2faeb20025bb7..0822bd6dfd6a8 100644 --- a/tests/run-make/bin-emit-no-symbols/rmake.rs +++ b/tests/run-make/bin-emit-no-symbols/rmake.rs @@ -14,5 +14,5 @@ fn main() { let out = llvm_readobj().input("app.o").arg("--symbols").run(); out.assert_stdout_contains("rust_begin_unwind"); out.assert_stdout_contains("rust_eh_personality"); - out.assert_stdout_contains("__rg_oom"); + out.assert_stdout_contains("__rust_alloc_error_handler"); } diff --git a/tests/ui/sanitizer/dataflow-abilist.txt b/tests/ui/sanitizer/dataflow-abilist.txt index 3d32397a175d4..b6fdfe3cbf6e7 100644 --- a/tests/ui/sanitizer/dataflow-abilist.txt +++ b/tests/ui/sanitizer/dataflow-abilist.txt @@ -490,16 +490,6 @@ fun:__dfso_*=uninstrumented fun:__dfso_*=discard # Rust functions. -fun:__rdl_alloc=uninstrumented -fun:__rdl_alloc_zeroed=uninstrumented -fun:__rdl_dealloc=uninstrumented -fun:__rdl_realloc=uninstrumented -fun:__rg_oom=uninstrumented -fun:__rust_alloc=uninstrumented -fun:__rust_alloc_error_handler=uninstrumented -fun:__rust_alloc_zeroed=uninstrumented -fun:__rust_dealloc=uninstrumented -fun:__rust_realloc=uninstrumented fun:_ZN4core*=uninstrumented fun:_ZN3std*=uninstrumented fun:rust_eh_personality=uninstrumented From 7e467cd132d0546c26be02d160ca097e00e67522 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 9 Oct 2025 14:08:55 +0000 Subject: [PATCH 04/12] Move computation of allocator shim contents to cg_ssa In the future this should make it easier to use weak symbols for the allocator shim on platforms that properly support weak symbols. And it would allow reusing the allocator shim code for handling default implementations of the upcoming externally implementable items feature on platforms that don't properly support weak symbols. --- compiler/rustc_ast/src/expand/allocator.rs | 3 + .../src/global_allocator.rs | 4 +- .../rustc_codegen_cranelift/src/allocator.rs | 79 +++++-------- compiler/rustc_codegen_gcc/src/allocator.rs | 64 +++++------ compiler/rustc_codegen_gcc/src/lib.rs | 7 +- compiler/rustc_codegen_llvm/src/allocator.rs | 104 ++++++++---------- compiler/rustc_codegen_llvm/src/lib.rs | 7 +- compiler/rustc_codegen_ssa/src/base.rs | 34 ++++-- .../rustc_codegen_ssa/src/traits/backend.rs | 5 +- 9 files changed, 139 insertions(+), 168 deletions(-) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index a3f9ddb154f71..6392cb77413dd 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -18,14 +18,17 @@ pub fn default_fn_name(base: Symbol) -> String { pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler; pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2"; +#[derive(Copy, Clone)] pub enum AllocatorTy { Layout, + Never, Ptr, ResultPtr, Unit, Usize, } +#[derive(Copy, Clone)] pub struct AllocatorMethod { pub name: Symbol, pub inputs: &'static [AllocatorMethodInput], diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 96bece2a368cd..c968353504e15 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -151,7 +151,7 @@ impl AllocFnFactory<'_, '_> { self.cx.expr_ident(self.span, ident) } - AllocatorTy::ResultPtr | AllocatorTy::Unit => { + AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { panic!("can't convert AllocatorTy to an argument") } } @@ -163,7 +163,7 @@ impl AllocFnFactory<'_, '_> { AllocatorTy::Unit => self.cx.ty(self.span, TyKind::Tup(ThinVec::new())), - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + AllocatorTy::Layout | AllocatorTy::Never | AllocatorTy::Usize | AllocatorTy::Ptr => { panic!("can't convert `AllocatorTy` to an output") } } diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 9533f870434ab..67b89114356b5 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -3,10 +3,9 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_ast::expand::allocator::{ - ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - default_fn_name, global_fn_name, + AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, }; -use rustc_codegen_ssa::base::allocator_kind_for_codegen; +use rustc_codegen_ssa::base::{allocator_kind_for_codegen, allocator_shim_contents}; use rustc_session::config::OomStrategy; use rustc_symbol_mangling::mangle_internal_symbol; @@ -15,74 +14,54 @@ use crate::prelude::*; /// Returns whether an allocator shim was created pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool { let Some(kind) = allocator_kind_for_codegen(tcx) else { return false }; - codegen_inner( - tcx, - module, - kind, - tcx.alloc_error_handler_kind(()).unwrap(), - tcx.sess.opts.unstable_opts.oom, - ); + let methods = allocator_shim_contents(tcx, kind); + codegen_inner(tcx, module, &methods, tcx.sess.opts.unstable_opts.oom); true } fn codegen_inner( tcx: TyCtxt<'_>, module: &mut dyn Module, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], oom_strategy: OomStrategy, ) { let usize_ty = module.target_config().pointer_type(); - if kind == AllocatorKind::Default { - for method in ALLOCATOR_METHODS { - let mut arg_tys = Vec::with_capacity(method.inputs.len()); - for input in method.inputs.iter() { - match input.ty { - AllocatorTy::Layout => { - arg_tys.push(usize_ty); // size - arg_tys.push(usize_ty); // align - } - AllocatorTy::Ptr => arg_tys.push(usize_ty), - AllocatorTy::Usize => arg_tys.push(usize_ty), - - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + for method in methods { + let mut arg_tys = Vec::with_capacity(method.inputs.len()); + for input in method.inputs.iter() { + match input.ty { + AllocatorTy::Layout => { + arg_tys.push(usize_ty); // size + arg_tys.push(usize_ty); // align } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(usize_ty), - AllocatorTy::Unit => None, + AllocatorTy::Ptr => arg_tys.push(usize_ty), + AllocatorTy::Usize => arg_tys.push(usize_ty), - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") + AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { + panic!("invalid allocator arg") } - }; - - let sig = Signature { - call_conv: module.target_config().default_call_conv, - params: arg_tys.iter().cloned().map(AbiParam::new).collect(), - returns: output.into_iter().map(AbiParam::new).collect(), - }; - crate::common::create_wrapper_function( - module, - sig, - &mangle_internal_symbol(tcx, &global_fn_name(method.name)), - &mangle_internal_symbol(tcx, &default_fn_name(method.name)), - ); + } } - } + let output = match method.output { + AllocatorTy::ResultPtr => Some(usize_ty), + AllocatorTy::Never | AllocatorTy::Unit => None, + + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; - if alloc_error_handler_kind == AllocatorKind::Default { let sig = Signature { call_conv: module.target_config().default_call_conv, - params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)], - returns: vec![], + params: arg_tys.iter().cloned().map(AbiParam::new).collect(), + returns: output.into_iter().map(AbiParam::new).collect(), }; crate::common::create_wrapper_function( module, sig, - &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), - &mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER)), + &mangle_internal_symbol(tcx, &global_fn_name(method.name)), + &mangle_internal_symbol(tcx, &default_fn_name(method.name)), ); } diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index 3f99612fcdb02..647569694b04d 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -2,8 +2,7 @@ use gccjit::FnAttribute; use gccjit::{Context, FunctionType, RValue, ToRValue, Type}; use rustc_ast::expand::allocator::{ - ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - default_fn_name, global_fn_name, + AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, }; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; @@ -18,8 +17,7 @@ pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, mods: &mut GccContext, _module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) { let context = &mods.context; let usize = match tcx.sess.target.pointer_width { @@ -31,46 +29,34 @@ pub(crate) unsafe fn codegen( let i8 = context.new_type::(); let i8p = i8.make_pointer(); - if kind == AllocatorKind::Default { - for method in ALLOCATOR_METHODS { - let mut types = Vec::with_capacity(method.inputs.len()); - for input in method.inputs.iter() { - match input.ty { - AllocatorTy::Layout => { - types.push(usize); - types.push(usize); - } - AllocatorTy::Ptr => types.push(i8p), - AllocatorTy::Usize => types.push(usize), - - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + for method in methods { + let mut types = Vec::with_capacity(method.inputs.len()); + for input in method.inputs.iter() { + match input.ty { + AllocatorTy::Layout => { + types.push(usize); + types.push(usize); } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(i8p), - AllocatorTy::Unit => None, + AllocatorTy::Ptr => types.push(i8p), + AllocatorTy::Usize => types.push(usize), - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") + AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { + panic!("invalid allocator arg") } - }; - let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); - let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - - create_wrapper_function(tcx, context, &from_name, Some(&to_name), &types, output); + } } - } + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Never | AllocatorTy::Unit => None, - if alloc_error_handler_kind == AllocatorKind::Default { - // FIXME(bjorn3): Add noreturn attribute - create_wrapper_function( - tcx, - context, - &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), - Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))), - &[usize, usize], - None, - ); + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; + let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); + let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); + + create_wrapper_function(tcx, context, &from_name, Some(&to_name), &types, output); } create_const_value_function( diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index ec7eab8489ab8..c85ed0ebb3390 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -92,7 +92,7 @@ use back::lto::{ThinBuffer, ThinData}; use gccjit::{CType, Context, OptimizationLevel}; #[cfg(feature = "master")] use gccjit::{TargetInfo, Version}; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn, @@ -284,8 +284,7 @@ impl ExtraBackendMethods for GccCodegenBackend { &self, tcx: TyCtxt<'_>, module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) -> Self::Module { let mut mods = GccContext { context: Arc::new(SyncContext::new(new_context(tcx))), @@ -295,7 +294,7 @@ impl ExtraBackendMethods for GccCodegenBackend { }; unsafe { - allocator::codegen(tcx, &mut mods, module_name, kind, alloc_error_handler_kind); + allocator::codegen(tcx, &mut mods, module_name, methods); } mods } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index b645db3f4a8b8..a7e83f65151ae 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -1,7 +1,6 @@ use libc::c_uint; use rustc_ast::expand::allocator::{ - ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, - default_fn_name, global_fn_name, + AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, }; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; @@ -21,8 +20,7 @@ pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, cx: SimpleCx<'_>, module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) { let usize = match tcx.sess.target.pointer_width { 16 => cx.type_i16(), @@ -33,67 +31,59 @@ pub(crate) unsafe fn codegen( let i8 = cx.type_i8(); let i8p = cx.type_ptr(); - if kind == AllocatorKind::Default { - for method in ALLOCATOR_METHODS { - let mut args = Vec::with_capacity(method.inputs.len()); - for input in method.inputs.iter() { - match input.ty { - AllocatorTy::Layout => { - args.push(usize); // size - args.push(usize); // align - } - AllocatorTy::Ptr => args.push(i8p), - AllocatorTy::Usize => args.push(usize), - - AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + for method in methods { + let mut args = Vec::with_capacity(method.inputs.len()); + for input in method.inputs.iter() { + match input.ty { + AllocatorTy::Layout => { + args.push(usize); // size + args.push(usize); // align } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(i8p), - AllocatorTy::Unit => None, + AllocatorTy::Ptr => args.push(i8p), + AllocatorTy::Usize => args.push(usize), - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("invalid allocator output") + AllocatorTy::Never | AllocatorTy::ResultPtr | AllocatorTy::Unit => { + panic!("invalid allocator arg") } - }; - - let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); - let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - - let alloc_attr_flag = match method.name { - sym::alloc => CodegenFnAttrFlags::ALLOCATOR, - sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR, - sym::realloc => CodegenFnAttrFlags::REALLOCATOR, - sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED, - _ => unreachable!("Unknown allocator method!"), - }; - - let mut attrs = CodegenFnAttrs::new(); - attrs.flags |= alloc_attr_flag; - create_wrapper_function( - tcx, - &cx, - &from_name, - Some(&to_name), - &args, - output, - false, - &attrs, - ); + } } - } - if alloc_error_handler_kind == AllocatorKind::Default { - // rust alloc error handler + let mut no_return = false; + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Unit => None, + AllocatorTy::Never => { + no_return = true; + None + } + + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; + + let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); + let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); + + let alloc_attr_flag = match method.name { + sym::alloc => CodegenFnAttrFlags::ALLOCATOR, + sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR, + sym::realloc => CodegenFnAttrFlags::REALLOCATOR, + sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED, + _ => CodegenFnAttrFlags::empty(), + }; + + let mut attrs = CodegenFnAttrs::new(); + attrs.flags |= alloc_attr_flag; create_wrapper_function( tcx, &cx, - &mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)), - Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))), - &[usize, usize], // size, align - None, - true, - &CodegenFnAttrs::new(), + &from_name, + Some(&to_name), + &args, + output, + no_return, + &attrs, ); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 807049f08d367..34fdb4a47c0ff 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -30,7 +30,7 @@ use back::write::{create_informational_target_machine, create_target_machine}; use context::SimpleCx; use errors::ParseTargetMachineConfig; use llvm_util::target_config; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, @@ -107,14 +107,13 @@ impl ExtraBackendMethods for LlvmCodegenBackend { &self, tcx: TyCtxt<'tcx>, module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) -> ModuleLlvm { let module_llvm = ModuleLlvm::new_metadata(tcx, module_name); let cx = SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size()); unsafe { - allocator::codegen(tcx, cx, module_name, kind, alloc_error_handler_kind); + allocator::codegen(tcx, cx, module_name, methods); } module_llvm } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index ac3bcb1ea269f..cd9fb098e531d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -6,7 +6,9 @@ use std::time::{Duration, Instant}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; use rustc_ast as ast; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::{ + ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorTy, +}; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; @@ -655,6 +657,26 @@ pub(crate) fn needs_allocator_shim_for_linking( !any_dynamic_crate } +pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec { + let mut methods = Vec::new(); + + if kind == AllocatorKind::Default { + methods.extend(ALLOCATOR_METHODS.into_iter().copied()); + } + + // If allocator_kind is Some then alloc_error_handler_kind must + // also be Some. + if tcx.alloc_error_handler_kind(()).unwrap() == AllocatorKind::Default { + methods.push(AllocatorMethod { + name: ALLOC_ERROR_HANDLER, + inputs: &[], + output: AllocatorTy::Never, + }); + } + + methods +} + pub fn codegen_crate( backend: B, tcx: TyCtxt<'_>, @@ -699,14 +721,8 @@ pub fn codegen_crate( cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); tcx.sess.time("write_allocator_module", || { - let module = backend.codegen_allocator( - tcx, - &llmod_id, - kind, - // If allocator_kind is Some then alloc_error_handler_kind must - // also be Some. - tcx.alloc_error_handler_kind(()).unwrap(), - ); + let module = + backend.codegen_allocator(tcx, &llmod_id, &allocator_shim_contents(tcx, kind)); Some(ModuleCodegen::new_allocator(llmod_id, module)) }) } else { diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 2400160075e2d..ec53d9f53eb83 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::hash::Hash; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::AllocatorMethod; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_metadata::EncodedMetadata; @@ -116,8 +116,7 @@ pub trait ExtraBackendMethods: &self, tcx: TyCtxt<'tcx>, module_name: &str, - kind: AllocatorKind, - alloc_error_handler_kind: AllocatorKind, + methods: &[AllocatorMethod], ) -> Self::Module; /// This generates the codegen unit and returns it along with From 2e25b5876fe7a9a06bb5a2248a2e54e86937e002 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:24:41 +0000 Subject: [PATCH 05/12] Add a couple of comments Co-Authored-By: Ralf Jung --- src/tools/miri/src/shims/foreign_items.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 467ac2805fe5e..39e32322f5b8d 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -51,6 +51,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Some shims forward to other MIR bodies. match link_name.as_str() { + // This allocator function has forwarding shims synthesized during normal codegen + // (see `allocator_shim_contents`); this is where we emulate that behavior. // FIXME should use global_fn_name, but mangle_internal_symbol requires a static str. name if name == this.mangle_internal_symbol("__rust_alloc_error_handler") => { // Forward to the right symbol that implements this function. @@ -68,6 +70,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .expect("missing alloc error handler symbol"); return interp_ok(Some(handler)); } + // Fall through to the `lookup_exported_symbol` below which should find + // a `__rust_alloc_error_handler`. } _ => {} } From 88e98206836bcf80b33dce9d77298c86c8778e96 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:51:52 +0000 Subject: [PATCH 06/12] Fix review comments --- compiler/rustc_ast/src/expand/allocator.rs | 1 + compiler/rustc_codegen_ssa/src/base.rs | 4 ++-- compiler/rustc_metadata/src/creader.rs | 20 +++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index 6392cb77413dd..336126a01f47e 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -18,6 +18,7 @@ pub fn default_fn_name(base: Symbol) -> String { pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler; pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2"; +/// Argument or return type for methods in the allocator shim #[derive(Copy, Clone)] pub enum AllocatorTy { Layout, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index cd9fb098e531d..1a79038d1fcd1 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -664,8 +664,8 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec !spans.is_empty(), }; - self.has_alloc_error_handler = - match &*fn_spans(krate, Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER))) { - [span1, span2, ..] => { - tcx.dcx().emit_err(errors::NoMultipleAllocErrorHandler { - span2: *span2, - span1: *span1, - }); - true - } - spans => !spans.is_empty(), - }; + let alloc_error_handler = Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER)); + self.has_alloc_error_handler = match &*fn_spans(krate, alloc_error_handler) { + [span1, span2, ..] => { + tcx.dcx() + .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 }); + true + } + spans => !spans.is_empty(), + }; // Check to see if we actually need an allocator. This desire comes // about through the `#![needs_allocator]` attribute and is typically From 714843dc57f58e144862bd3f34416e1dd6b15167 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Tue, 14 Oct 2025 06:13:22 +0000 Subject: [PATCH 07/12] replace manual implementation with carrying_mul_add --- library/core/src/num/bignum.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index f21fe0b4438fb..33b1e8cb56db2 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -38,9 +38,8 @@ macro_rules! impl_full_ops { fn full_mul_add(self, other: $ty, other2: $ty, carry: $ty) -> ($ty, $ty) { // This cannot overflow; // the output is between `0` and `2^nbits * (2^nbits - 1)`. - let v = (self as $bigty) * (other as $bigty) + (other2 as $bigty) + - (carry as $bigty); - ((v >> <$ty>::BITS) as $ty, v as $ty) + let (lo, hi) = self.carrying_mul_add(other, other2, carry); + (hi, lo) } fn full_div_rem(self, other: $ty, borrow: $ty) -> ($ty, $ty) { From 8a145efc70f0eea89de9fae314cb37b64c1f2b75 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 13 Sep 2025 13:01:11 +0200 Subject: [PATCH 08/12] std: improve handling of timed condition variable waits on macOS --- library/Cargo.lock | 4 +- library/std/Cargo.toml | 2 +- library/std/src/sys/pal/unix/sync/condvar.rs | 99 ++++++++++++++++---- library/std/tests/sync/condvar.rs | 32 +++++++ 4 files changed, 115 insertions(+), 22 deletions(-) diff --git a/library/Cargo.lock b/library/Cargo.lock index 47fbf5169f491..b5b534c986b99 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.175" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" dependencies = [ "rustc-std-workspace-core", ] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 779b07ce240a6..d4108a57acaf0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -33,7 +33,7 @@ miniz_oxide = { version = "0.8.0", optional = true, default-features = false } addr2line = { version = "0.25.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.172", default-features = false, features = [ +libc = { version = "0.2.177", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index efa6f8d776559..b6c3ba4136f2a 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -1,11 +1,6 @@ use super::Mutex; use crate::cell::UnsafeCell; use crate::pin::Pin; -#[cfg(not(target_os = "nto"))] -use crate::sys::pal::time::TIMESPEC_MAX; -#[cfg(target_os = "nto")] -use crate::sys::pal::time::TIMESPEC_MAX_CAPPED; -use crate::sys::pal::time::Timespec; use crate::time::Duration; pub struct Condvar { @@ -47,27 +42,29 @@ impl Condvar { let r = unsafe { libc::pthread_cond_wait(self.raw(), mutex.raw()) }; debug_assert_eq!(r, 0); } +} +#[cfg(not(target_vendor = "apple"))] +impl Condvar { /// # Safety /// * `init` must have been called on this instance. /// * `mutex` must be locked by the current thread. /// * This condition variable may only be used with the same mutex. pub unsafe fn wait_timeout(&self, mutex: Pin<&Mutex>, dur: Duration) -> bool { + #[cfg(not(target_os = "nto"))] + use crate::sys::pal::time::TIMESPEC_MAX; + #[cfg(target_os = "nto")] + use crate::sys::pal::time::TIMESPEC_MAX_CAPPED; + use crate::sys::pal::time::Timespec; + let mutex = mutex.raw(); - // OSX implementation of `pthread_cond_timedwait` is buggy - // with super long durations. When duration is greater than - // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` - // in macOS Sierra returns error 316. - // - // This program demonstrates the issue: - // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c - // - // To work around this issue, the timeout is clamped to 1000 years. - // - // Cygwin implementation is based on NT API and a super large timeout - // makes the syscall block forever. - #[cfg(any(target_vendor = "apple", target_os = "cygwin"))] + // Cygwin's implementation is based on the NT API, which measures time + // in units of 100 ns. Unfortunately, Cygwin does not properly guard + // against overflow when converting the time, hence we clamp the interval + // to 1000 years, which will only become a problem in around 27000 years, + // when the next rollover is less than 1000 years away... + #[cfg(target_os = "cygwin")] let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur); @@ -84,6 +81,57 @@ impl Condvar { } } +// Apple platforms (since macOS version 10.4 and iOS version 2.0) have +// `pthread_cond_timedwait_relative_np`, a non-standard extension that +// measures timeouts based on the monotonic clock and is thus resilient +// against wall-clock changes. +#[cfg(target_vendor = "apple")] +impl Condvar { + /// # Safety + /// * `init` must have been called on this instance. + /// * `mutex` must be locked by the current thread. + /// * This condition variable may only be used with the same mutex. + pub unsafe fn wait_timeout(&self, mutex: Pin<&Mutex>, dur: Duration) -> bool { + let mutex = mutex.raw(); + + // The macOS implementation of `pthread_cond_timedwait` internally + // converts the timeout passed to `pthread_cond_timedwait_relative_np` + // to nanoseconds. Unfortunately, the "psynch" variant of condvars does + // not guard against overflow during the conversion[^1], which means + // that `pthread_cond_timedwait_relative_np` will return `ETIMEDOUT` + // much earlier than expected if the relative timeout is longer than + // `u64::MAX` nanoseconds. + // + // This can be observed even on newer platforms (by setting the environment + // variable PTHREAD_MUTEX_USE_ULOCK to a value other than "1") by calling e.g. + // ``` + // condvar.wait_timeout(..., Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)); + // ``` + // (see #37440, especially + // https://github.com/rust-lang/rust/issues/37440#issuecomment-3285958326). + // + // To work around this issue, always clamp the timeout to u64::MAX nanoseconds, + // even if the "ulock" variant is used (which does guard against overflow). + // + // [^1]: https://github.com/apple-oss-distributions/libpthread/blob/1ebf56b3a702df53213c2996e5e128a535d2577e/kern/kern_synch.c#L1269 + const MAX_DURATION: Duration = Duration::from_nanos(u64::MAX); + + let (dur, clamped) = if dur <= MAX_DURATION { (dur, false) } else { (MAX_DURATION, true) }; + + let timeout = libc::timespec { + // This cannot overflow because of the clamping above. + tv_sec: dur.as_secs() as i64, + tv_nsec: dur.subsec_nanos() as i64, + }; + + let r = unsafe { libc::pthread_cond_timedwait_relative_np(self.raw(), mutex, &timeout) }; + assert!(r == libc::ETIMEDOUT || r == 0); + // Report clamping as a spurious wakeup. Who knows, maybe some + // interstellar space probe will rely on this ;-). + r == 0 || clamped + } +} + #[cfg(not(any( target_os = "android", target_vendor = "apple", @@ -125,10 +173,23 @@ impl Condvar { } } +#[cfg(target_vendor = "apple")] +impl Condvar { + // `pthread_cond_timedwait_relative_np` measures the timeout + // based on the monotonic clock. + pub const PRECISE_TIMEOUT: bool = true; + + /// # Safety + /// May only be called once per instance of `Self`. + pub unsafe fn init(self: Pin<&mut Self>) { + // `PTHREAD_COND_INITIALIZER` is fully supported and we don't need to + // change clocks, so there's nothing to do here. + } +} + // `pthread_condattr_setclock` is unfortunately not supported on these platforms. #[cfg(any( target_os = "android", - target_vendor = "apple", target_os = "espidf", target_os = "horizon", target_os = "l4re", diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index 1b1c33efad58e..2a525f9b5e948 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -267,3 +267,35 @@ nonpoison_and_poison_unwrap_test!( } } ); + +// Some platforms internally cast the timeout duration into nanoseconds. +// If they fail to consider overflow during the conversion (I'm looking +// at you, macOS), `wait_timeout` will return immediately and indicate a +// timeout for durations that are slightly longer than u64::MAX nanoseconds. +// `std` should guard against this by clamping the timeout. +// See #37440 for context. +nonpoison_and_poison_unwrap_test!( + name: timeout_nanoseconds, + test_body: { + use locks::Mutex; + use locks::Condvar; + + let sent = Mutex::new(false); + let cond = Condvar::new(); + + thread::scope(|s| { + s.spawn(|| { + thread::sleep(Duration::from_secs(2)); + maybe_unwrap(sent.set(true)); + cond.notify_all(); + }); + + let guard = maybe_unwrap(sent.lock()); + // If there is internal overflow, this call will return almost + // immediately, before the other thread has reached the `notify_all` + let (guard, res) = maybe_unwrap(cond.wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)))); + assert!(!res.timed_out()); + assert!(*guard); + }) + } +); From fe1238e69693c4d466d1ec6d8b70e89e220d204b Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 13 Sep 2025 17:27:54 +0200 Subject: [PATCH 09/12] miri: shim `pthread_cond_timedwait_relative_np` --- .../miri/src/shims/unix/foreign_items.rs | 2 +- .../src/shims/unix/macos/foreign_items.rs | 6 +++ src/tools/miri/src/shims/unix/sync.rs | 22 +++++++--- ...thread_cond_timedwait_relative_np_apple.rs | 41 +++++++++++++++++++ 4 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 src/tools/miri/tests/pass-dep/libc/pthread_cond_timedwait_relative_np_apple.rs diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 55906f4eb9548..d04ef5eac541b 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -815,7 +815,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_cond_timedwait" => { let [cond, mutex, abstime] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - this.pthread_cond_timedwait(cond, mutex, abstime, dest)?; + this.pthread_cond_timedwait(cond, mutex, abstime, dest, /* macos_relative_np */ false)?; } "pthread_cond_destroy" => { let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 297d903c6ba4e..0754eb45a2d29 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -307,6 +307,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.os_unfair_lock_assert_not_owner(lock_op)?; } + "pthread_cond_timedwait_relative_np" => { + let [cond, mutex, reltime] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.pthread_cond_timedwait(cond, mutex, reltime, dest, /* macos_relative_np */ true)?; + } + _ => return interp_ok(EmulateItemResult::NotSupported), }; diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index cefd8b81ac180..a712279d57628 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -834,8 +834,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, cond_op: &OpTy<'tcx>, mutex_op: &OpTy<'tcx>, - abstime_op: &OpTy<'tcx>, + timeout_op: &OpTy<'tcx>, dest: &MPlaceTy<'tcx>, + macos_relative_np: bool, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -844,7 +845,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Extract the timeout. let duration = match this - .read_timespec(&this.deref_pointer_as(abstime_op, this.libc_ty_layout("timespec"))?)? + .read_timespec(&this.deref_pointer_as(timeout_op, this.libc_ty_layout("timespec"))?)? { Some(duration) => duration, None => { @@ -853,14 +854,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(()); } }; - if data.clock == TimeoutClock::RealTime { - this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; - } + + let (clock, anchor) = if macos_relative_np { + // `pthread_cond_timedwait_relative_np` always measures time against the + // monotonic clock, regardless of the condvar clock. + (TimeoutClock::Monotonic, TimeoutAnchor::Relative) + } else { + if data.clock == TimeoutClock::RealTime { + this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; + } + + (data.clock, TimeoutAnchor::Absolute) + }; this.condvar_wait( data.condvar_ref, mutex_ref, - Some((data.clock, TimeoutAnchor::Absolute, duration)), + Some((clock, anchor, duration)), Scalar::from_i32(0), this.eval_libc("ETIMEDOUT"), // retval_timeout dest.clone(), diff --git a/src/tools/miri/tests/pass-dep/libc/pthread_cond_timedwait_relative_np_apple.rs b/src/tools/miri/tests/pass-dep/libc/pthread_cond_timedwait_relative_np_apple.rs new file mode 100644 index 0000000000000..bcf06211b8c41 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/pthread_cond_timedwait_relative_np_apple.rs @@ -0,0 +1,41 @@ +//@only-target: apple # `pthread_cond_timedwait_relative_np` is a non-standard extension + +use std::time::Instant; + +// FIXME: remove once this is in libc. +mod libc { + pub use ::libc::*; + unsafe extern "C" { + pub unsafe fn pthread_cond_timedwait_relative_np( + cond: *mut libc::pthread_cond_t, + lock: *mut libc::pthread_mutex_t, + timeout: *const libc::timespec, + ) -> libc::c_int; + } +} + +fn main() { + unsafe { + let mut mutex: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER; + let mut cond: libc::pthread_cond_t = libc::PTHREAD_COND_INITIALIZER; + + // Wait for 100 ms. + let timeout = libc::timespec { tv_sec: 0, tv_nsec: 100_000_000 }; + + assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); + + let current_time = Instant::now(); + assert_eq!( + libc::pthread_cond_timedwait_relative_np(&mut cond, &mut mutex, &timeout), + libc::ETIMEDOUT + ); + let elapsed_time = current_time.elapsed().as_millis(); + // This is actually deterministic (since isolation remains enabled), + // but can change slightly with Rust updates. + assert!(90 <= elapsed_time && elapsed_time <= 110); + + assert_eq!(libc::pthread_mutex_unlock(&mut mutex), 0); + assert_eq!(libc::pthread_mutex_destroy(&mut mutex), 0); + assert_eq!(libc::pthread_cond_destroy(&mut cond), 0); + } +} From c00b4ba5efbaa80785cb58dca0389d0e6d7af284 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 14 Oct 2025 22:29:56 +0800 Subject: [PATCH 10/12] Fix ICE caused by associated_item_def_ids on wrong type in resolve diag --- compiler/rustc_resolve/src/lib.rs | 50 ++++++++++++------- .../struct-fields-ice-147325.rs | 11 ++++ .../struct-fields-ice-147325.stderr | 22 ++++++++ 3 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 tests/ui/structs/default-field-values/struct-fields-ice-147325.rs create mode 100644 tests/ui/structs/default-field-values/struct-fields-ice-147325.stderr diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index d9c3f4089a0fb..efb4bbfa2b72e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2332,30 +2332,44 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn field_idents(&self, def_id: DefId) -> Option> { match def_id.as_local() { Some(def_id) => self.field_names.get(&def_id).cloned(), - None => Some( - self.tcx - .associated_item_def_ids(def_id) - .iter() - .map(|&def_id| { - Ident::new(self.tcx.item_name(def_id), self.tcx.def_span(def_id)) - }) - .collect(), - ), + None if matches!( + self.tcx.def_kind(def_id), + DefKind::Struct | DefKind::Union | DefKind::Variant + ) => + { + Some( + self.tcx + .associated_item_def_ids(def_id) + .iter() + .map(|&def_id| { + Ident::new(self.tcx.item_name(def_id), self.tcx.def_span(def_id)) + }) + .collect(), + ) + } + _ => None, } } fn field_defaults(&self, def_id: DefId) -> Option> { match def_id.as_local() { Some(def_id) => self.field_defaults.get(&def_id).cloned(), - None => Some( - self.tcx - .associated_item_def_ids(def_id) - .iter() - .filter_map(|&def_id| { - self.tcx.default_field(def_id).map(|_| self.tcx.item_name(def_id)) - }) - .collect(), - ), + None if matches!( + self.tcx.def_kind(def_id), + DefKind::Struct | DefKind::Union | DefKind::Variant + ) => + { + Some( + self.tcx + .associated_item_def_ids(def_id) + .iter() + .filter_map(|&def_id| { + self.tcx.default_field(def_id).map(|_| self.tcx.item_name(def_id)) + }) + .collect(), + ) + } + _ => None, } } diff --git a/tests/ui/structs/default-field-values/struct-fields-ice-147325.rs b/tests/ui/structs/default-field-values/struct-fields-ice-147325.rs new file mode 100644 index 0000000000000..fc68060a96271 --- /dev/null +++ b/tests/ui/structs/default-field-values/struct-fields-ice-147325.rs @@ -0,0 +1,11 @@ +// ICE #147325: When the user mistakenly uses struct syntax to construct an enum, +// the field_idents and field_defaults functions will trigger an error + +mod m { + struct Priv1; +} + +fn main() { + Option { field1: m::Priv1 } //~ ERROR expected struct, variant or union type, found enum + //~^ ERROR unit struct `Priv1` is private +} diff --git a/tests/ui/structs/default-field-values/struct-fields-ice-147325.stderr b/tests/ui/structs/default-field-values/struct-fields-ice-147325.stderr new file mode 100644 index 0000000000000..7193a7112b445 --- /dev/null +++ b/tests/ui/structs/default-field-values/struct-fields-ice-147325.stderr @@ -0,0 +1,22 @@ +error[E0574]: expected struct, variant or union type, found enum `Option` + --> $DIR/struct-fields-ice-147325.rs:9:5 + | +LL | Option { field1: m::Priv1 } + | ^^^^^^ not a struct, variant or union type + +error[E0603]: unit struct `Priv1` is private + --> $DIR/struct-fields-ice-147325.rs:9:25 + | +LL | Option { field1: m::Priv1 } + | ^^^^^ private unit struct + | +note: the unit struct `Priv1` is defined here + --> $DIR/struct-fields-ice-147325.rs:5:5 + | +LL | struct Priv1; + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0574, E0603. +For more information about an error, try `rustc --explain E0574`. From a51a793472fc6ba16247ddc06e366f7993d8ae93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 14 Oct 2025 17:27:38 +0200 Subject: [PATCH 11/12] only check duplicates on old/unparsed attributes --- compiler/rustc_passes/src/check_attr.rs | 33 ++++++++++++++++--------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cba6243fa1098..5f0d7cdb59ef0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -393,8 +393,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); - if hir_id != CRATE_HIR_ID { match attr { Attribute::Parsed(_) => { /* Already validated. */ } @@ -440,8 +438,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - if let Some(BuiltinAttribute { duplicates, .. }) = builtin { - check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen); + if let Attribute::Unparsed(unparsed_attr) = attr + && let Some(BuiltinAttribute { duplicates, .. }) = + attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) + { + check_duplicates( + self.tcx, + unparsed_attr.span, + attr, + hir_id, + *duplicates, + &mut seen, + ); } self.check_unused_attribute(hir_id, attr, style) @@ -2481,6 +2489,7 @@ pub(crate) fn provide(providers: &mut Providers) { // FIXME(jdonszelmann): remove, check during parsing fn check_duplicates( tcx: TyCtxt<'_>, + attr_span: Span, attr: &Attribute, hir_id: HirId, duplicates: AttributeDuplicates, @@ -2497,10 +2506,10 @@ fn check_duplicates( match seen.entry(attr_name) { Entry::Occupied(mut entry) => { let (this, other) = if matches!(duplicates, FutureWarnPreceding) { - let to_remove = entry.insert(attr.span()); - (to_remove, attr.span()) + let to_remove = entry.insert(attr_span); + (to_remove, attr_span) } else { - (attr.span(), *entry.get()) + (attr_span, *entry.get()) }; tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, @@ -2517,22 +2526,22 @@ fn check_duplicates( ); } Entry::Vacant(entry) => { - entry.insert(attr.span()); + entry.insert(attr_span); } } } ErrorFollowing | ErrorPreceding => match seen.entry(attr_name) { Entry::Occupied(mut entry) => { let (this, other) = if matches!(duplicates, ErrorPreceding) { - let to_remove = entry.insert(attr.span()); - (to_remove, attr.span()) + let to_remove = entry.insert(attr_span); + (to_remove, attr_span) } else { - (attr.span(), *entry.get()) + (attr_span, *entry.get()) }; tcx.dcx().emit_err(errors::UnusedMultiple { this, other, name: attr_name }); } Entry::Vacant(entry) => { - entry.insert(attr.span()); + entry.insert(attr_span); } }, } From cf0256008be86edbeac0af4300e67d1f7bcb7df9 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:53:40 +0000 Subject: [PATCH 12/12] Add comment to AllocatorKind and AllocatorMethod --- compiler/rustc_ast/src/expand/allocator.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index 336126a01f47e..c200921e5f807 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -3,7 +3,9 @@ use rustc_span::{Symbol, sym}; #[derive(Clone, Debug, Copy, Eq, PartialEq, HashStable_Generic)] pub enum AllocatorKind { + /// Use `#[global_allocator]` as global allocator. Global, + /// Use the default implementation in libstd as global allocator. Default, } @@ -29,6 +31,7 @@ pub enum AllocatorTy { Usize, } +/// A method that will be codegened in the allocator shim. #[derive(Copy, Clone)] pub struct AllocatorMethod { pub name: Symbol,