From 27f97d7f7162f8f6c2250e6778e462e3738ed285 Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Tue, 3 Feb 2026 07:19:56 -0800 Subject: [PATCH 1/4] RwLock: refine documentation to emphasize non-reentrancy guarantees This addresses the need for clarification brought up in an issue. Specifically, it notes that some implementations may choose to panic if they detect deadlock situations during recursive locking attempts for both `read()` and `write()` calls. * Provide an example highlighting that multiple read locks can be held across different threads simultaneously. * Remove the example that shows a situation that can potentially deadlock. (as demonstrated in the very same documentation a few paragraphs above) * Improve documentation regarding the possibility of panics during recursive read or write lock attempts. Issues: Ambiguity in RwLock documentation about multiple read() calls... Signed-off-by: Henner Zeller --- library/std/src/sync/poison/rwlock.rs | 48 +++++++++++++++++---------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index acfeb96900cc9..c01ee17eecda3 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -56,24 +56,36 @@ use crate::sys::sync as sys; /// # Examples /// /// ``` -/// use std::sync::RwLock; +/// use std::sync::{Arc, RwLock}; +/// use std::thread; +/// use std::time::Duration; /// -/// let lock = RwLock::new(5); +/// let data = Arc::new(RwLock::new(5)); /// -/// // many reader locks can be held at once -/// { -/// let r1 = lock.read().unwrap(); -/// let r2 = lock.read().unwrap(); -/// assert_eq!(*r1, 5); -/// assert_eq!(*r2, 5); -/// } // read locks are dropped at this point +/// // Multiple readers can access in parallel. +/// for i in 0..3 { +/// let lock_clone = Arc::clone(&data); /// -/// // only one write lock may be held, however -/// { -/// let mut w = lock.write().unwrap(); -/// *w += 1; -/// assert_eq!(*w, 6); -/// } // write lock is dropped here +/// thread::spawn(move || { +/// let value = lock_clone.read().unwrap(); +/// +/// println!("Reader {}: Read value {}, now holding lock...", i, *value); +/// +/// // Simulating a long read operation +/// thread::sleep(Duration::from_secs(1)); +/// +/// println!("Reader {}: Dropping lock.", i); +/// // Read lock unlocked when going out of scope. +/// }); +/// } +/// +/// thread::sleep(Duration::from_millis(100)); // Wait for readers to start +/// +/// // While all readers can proceed, a call to .write() has to wait for +// // current active reader locks. +/// let mut writable_data = data.write().unwrap(); +/// println!("Writer proceeds..."); +/// *writable_data += 1; /// ``` /// /// [`Mutex`]: super::Mutex @@ -370,7 +382,8 @@ impl RwLock { /// /// # Panics /// - /// This function might panic when called if the lock is already held by the current thread. + /// This function might panic when called if the lock is already held by the current thread + /// in read or write mode. /// /// # Examples /// @@ -467,7 +480,8 @@ impl RwLock { /// /// # Panics /// - /// This function might panic when called if the lock is already held by the current thread. + /// This function might panic when called if the lock is already held by the current thread + /// in read or write mode. /// /// # Examples /// From 86daea3b52d5ac2722aa33556319abbd1bbfc828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 5 Feb 2026 15:57:14 +0100 Subject: [PATCH 2/4] Port rustc_reservation_impl to the new attribute parser --- .../src/attributes/rustc_internal.rs | 27 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 1 + .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_analysis/src/collect.rs | 3 ++- compiler/rustc_passes/src/check_attr.rs | 2 +- .../src/traits/coherence.rs | 9 +++---- .../src/traits/select/mod.rs | 9 +++---- 8 files changed, 43 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 3b7d25b43ba2f..9b695818a2bf5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -490,6 +490,7 @@ impl CombineAttributeParser for RustcMirParser { .collect() } } + pub(crate) struct RustcNonConstTraitMethodParser; impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { @@ -810,3 +811,29 @@ impl SingleAttributeParser for RustcDefPath { Some(AttributeKind::RustcDefPath(cx.attr_span)) } } + +pub(crate) struct RustcReservationImplParser; + +impl SingleAttributeParser for RustcReservationImplParser { + const PATH: &[Symbol] = &[sym::rustc_reservation_impl]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Impl { of_trait: true })]); + + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "reservation message"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(args.span().unwrap_or(cx.attr_span), None); + return None; + }; + + let Some(value_str) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + Some(AttributeKind::RustcReservationImpl(cx.attr_span, value_str)) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 5b489d3064990..8383df38bffb8 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -203,6 +203,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fa781a74c9e27..c0566adcc62c4 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1198,6 +1198,9 @@ pub enum AttributeKind { /// Represents `#[rustc_reallocator]` RustcReallocator, + /// Represents `#[rustc_reservation_impl]` + RustcReservationImpl(Span, Symbol), + /// Represents `#[rustc_scalable_vector(N)]` RustcScalableVector { /// The base multiple of lanes that are in a scalable vector, if provided. `element_count` diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 14b54c129d3ea..459b269421e66 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -144,6 +144,7 @@ impl AttributeKind { RustcPreserveUbChecks => No, RustcPubTransparent(..) => Yes, RustcReallocator => No, + RustcReservationImpl(..) => Yes, RustcScalableVector { .. } => Yes, RustcShouldNotBeCalledOnConstItems(..) => Yes, RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index f50aff187f252..2996d47510eed 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1274,7 +1274,8 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader .of_trait .unwrap_or_else(|| panic!("expected impl trait, found inherent impl on {def_id:?}")); let selfty = tcx.type_of(def_id).instantiate_identity(); - let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); + let is_rustc_reservation = + find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(..)); check_impl_constness(tcx, impl_.constness, &of_trait.trait_ref); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 680a93908354c..cbfb7b1585ef3 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -335,6 +335,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) | AttributeKind::RustcPreserveUbChecks | AttributeKind::RustcReallocator + | AttributeKind::RustcReservationImpl(..) | AttributeKind::RustcScalableVector { .. } | AttributeKind::RustcShouldNotBeCalledOnConstItems(..) | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) @@ -391,7 +392,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_trivial_field_reads | sym::rustc_on_unimplemented | sym::rustc_do_not_const_check - | sym::rustc_reservation_impl | sym::rustc_doc_primitive | sym::rustc_conversion_suggestion | sym::rustc_deprecated_safe_2024 diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 9f59f6c592509..fc628e78a3e23 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -8,8 +8,10 @@ use std::fmt::Debug; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::{Diag, EmissionGuarantee}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; +use rustc_hir::find_attr; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::PredicateObligations; use rustc_macros::{TypeFoldable, TypeVisitable}; @@ -23,7 +25,7 @@ use rustc_middle::ty::{ }; pub use rustc_next_trait_solver::coherence::*; use rustc_next_trait_solver::solve::SolverDelegateEvalExt; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, warn}; use super::ObligationCtxt; @@ -758,10 +760,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { } = cand.kind() && let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { - let message = infcx - .tcx - .get_attr(def_id, sym::rustc_reservation_impl) - .and_then(|a| a.value_str()); + let message = find_attr!(infcx.tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(_, message) => *message); if let Some(message) = message { self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 787dd4ea62549..4469afc3a4ebf 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -12,9 +12,9 @@ use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Diag, EmissionGuarantee}; -use rustc_hir as hir; -use rustc_hir::LangItem; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::at::ToTrace; @@ -33,7 +33,7 @@ use rustc_middle::ty::{ may_use_unstable_feature, }; use rustc_next_trait_solver::solve::AliasBoundKind; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use tracing::{debug, instrument, trace}; use self::EvaluationResult::*; @@ -1445,8 +1445,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { - let message = - tcx.get_attr(def_id, sym::rustc_reservation_impl).and_then(|a| a.value_str()); + let message = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(_, message) => *message); if let Some(message) = message { debug!( "filter_reservation_impls: \ From 26bd9048dd922984626b0e684c17f6e02e3165c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 7 Feb 2026 14:42:17 +0100 Subject: [PATCH 3/4] Port rustc_insignificant_dtor to the new attribute parser --- .../src/attributes/rustc_internal.rs | 13 +++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 1 + compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_ty_utils/src/needs_drop.rs | 6 +++--- 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 3b7d25b43ba2f..d4df14242b01c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -722,6 +722,19 @@ impl CombineAttributeParser for RustcThenThisWouldNeedParser { } } +pub(crate) struct RustcInsignificantDtorParser; + +impl NoArgsAttributeParser for RustcInsignificantDtorParser { + const PATH: &[Symbol] = &[sym::rustc_insignificant_dtor]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Enum), + Allow(Target::Struct), + Allow(Target::ForeignTy), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcInsignificantDtor; +} + pub(crate) struct RustcEffectiveVisibilityParser; impl NoArgsAttributeParser for RustcEffectiveVisibilityParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 5b489d3064990..c5e4e8cb0f0de 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -264,6 +264,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fa781a74c9e27..785374c05cb7d 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1120,6 +1120,9 @@ pub enum AttributeKind { /// Represents `#[rustc_if_this_changed]` RustcIfThisChanged(Span, Option), + /// Represents `#[rustc_insignificant_dtor]` + RustcInsignificantDtor, + /// Represents `#[rustc_layout]` RustcLayout(ThinVec), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 14b54c129d3ea..0961229e74ae7 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -118,6 +118,7 @@ impl AttributeKind { RustcHasIncoherentInherentImpls => Yes, RustcHiddenTypeOfOpaques => No, RustcIfThisChanged(..) => No, + RustcInsignificantDtor => Yes, RustcLayout(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 680a93908354c..d196bf6530d99 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -313,6 +313,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::RustcHiddenTypeOfOpaques | AttributeKind::RustcIfThisChanged(..) + | AttributeKind::RustcInsignificantDtor | AttributeKind::RustcLayout(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) | AttributeKind::RustcLayoutScalarValidRangeStart(..) @@ -383,7 +384,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::default_lib_allocator | sym::rustc_diagnostic_item | sym::rustc_no_mir_inline - | sym::rustc_insignificant_dtor | sym::rustc_nonnull_optimization_guaranteed | sym::rustc_intrinsic | sym::rustc_inherit_overflow_checks diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 0ef435b1a0e21..06eef7e95145e 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -1,13 +1,14 @@ //! Check whether a type has (potentially) non-trivial drop glue. use rustc_data_structures::fx::FxHashSet; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; +use rustc_hir::find_attr; use rustc_hir::limit::Limit; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::util::{AlwaysRequiresDrop, needs_drop_components}; use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt}; -use rustc_span::sym; use tracing::{debug, instrument}; use crate::errors::NeedsDropOverflow; @@ -396,8 +397,7 @@ fn adt_consider_insignificant_dtor<'tcx>( tcx: TyCtxt<'tcx>, ) -> impl Fn(ty::AdtDef<'tcx>) -> Option { move |adt_def: ty::AdtDef<'tcx>| { - let is_marked_insig = tcx.has_attr(adt_def.did(), sym::rustc_insignificant_dtor); - if is_marked_insig { + if find_attr!(tcx.get_all_attrs(adt_def.did()), AttributeKind::RustcInsignificantDtor) { // In some cases like `std::collections::HashMap` where the struct is a wrapper around // a type that is a Drop type, and the wrapped type (eg: `hashbrown::HashMap`) lies // outside stdlib, we might choose to still annotate the wrapper (std HashMap) with From 071935e2a3d82a30f590838019ecf46d7aa73697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 7 Feb 2026 15:06:32 +0100 Subject: [PATCH 4/4] remove from impl block in std --- library/alloc/src/ffi/c_str.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index d6dcba7107a9c..fba967c04895a 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -103,6 +103,7 @@ use crate::vec::Vec; /// and other memory errors. #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] #[rustc_diagnostic_item = "cstring_type"] +#[rustc_insignificant_dtor] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub struct CString { // Invariant 1: the slice ends with a zero byte and has a length of at least one. @@ -694,7 +695,6 @@ impl CString { // memory-unsafe code from working by accident. Inline // to prevent LLVM from optimizing it away in debug builds. #[stable(feature = "cstring_drop", since = "1.13.0")] -#[rustc_insignificant_dtor] impl Drop for CString { #[inline] fn drop(&mut self) {